Skip to main content
ThunderPhone is prepaid. Each organization carries a USD balance that decrements as calls are placed; if the balance reaches $0.00, inbound calls are rejected and outbound calls return 402 Payment Required. These endpoints let you query balance state, top up via Stripe, and configure automatic reloads.

Endpoints

MethodPathDescription
GET/v1/billingGet billing account state
PATCH/v1/billingUpdate auto-reload settings
POST/v1/billing/top-upCreate a top-up payment intent (one-time charge)
POST/v1/billing/setup-intentCreate a Stripe SetupIntent for saving a card
POST/v1/billing/default-payment-methodSet the default payment method
GET/v1/billing/transactionsList balance transactions

Billing account object

{
  "balance_cents": 125000,
  "balance_usd": "1250.00",
  "currency": "usd",
  "auto_reload_enabled": true,
  "auto_reload_minimum_cents": 1000,
  "auto_reload_amount_cents": 5000,
  "auto_reload_monthly_limit_cents": null,
  "has_payment_method": true
}
FieldTypeDescription
balance_centsintegerCurrent balance in USD cents
balance_usdstringFormatted, e.g. "1250.00"
currencystringAlways usd
auto_reload_enabledbooleanWhether automatic top-ups are on
auto_reload_minimum_centsintegerBalance threshold that triggers a reload
auto_reload_amount_centsintegerAmount to charge on reload
auto_reload_monthly_limit_centsinteger | nullCap on auto-reload spend per calendar month. null = no cap
has_payment_methodbooleanDerived — true iff a default Stripe payment method is set

Get billing account

curl https://api.thunderphone.com/v1/billing \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
Returns 200 OK with a Billing account object.

Update auto-reload

Enable, disable, or reconfigure automatic balance reloads. A payment method must be on file (has_payment_method: true) before auto_reload_enabled can be set to true — otherwise the server returns 422 Unprocessable Entity with code: "payment_method_required".
curl -X PATCH https://api.thunderphone.com/v1/billing \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "auto_reload_enabled": true,
    "auto_reload_minimum_cents": 1000,
    "auto_reload_amount_cents": 5000,
    "auto_reload_monthly_limit_cents": 50000
  }'
FieldTypeRequired when enabling
auto_reload_enabledboolean
auto_reload_minimum_centsinteger ≥ 0yes — triggers a reload once balance drops below
auto_reload_amount_centsinteger > 0yes — amount charged on reload
auto_reload_monthly_limit_centsinteger ≥ 0, or nullno — null means unlimited
Returns 200 OK with the updated Billing account object.

Top up balance

Create a one-time charge to increase your balance. Uses the default payment method unless payment_method_id is supplied.
curl -X POST https://api.thunderphone.com/v1/billing/top-up \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"amount_cents": 10000}'
FieldTypeRequiredDescription
amount_centsinteger ≥ 1yesUSD cents. Minimum typically $5
payment_method_idstringnoStripe PM id. Defaults to the org’s default
Returns 201 Created:
{
  "payment_intent_id": "pi_abc123",
  "status":            "succeeded",
  "client_secret":     "pi_abc123_secret_..."
}
FieldTypeDescription
payment_intent_idstringStripe PaymentIntent id
statusstringMirrors Stripe’s own PaymentIntent.status — typically succeeded, requires_action, or requires_payment_method
client_secretstringPass to Stripe.js / stripe-react on the client side if status === "requires_action" (3DS)
When status === "succeeded" the balance is credited synchronously from the request. When status === "requires_action", complete the 3DS flow client-side and wait for the payment_intent.succeeded webhook — then poll GET /v1/billing to observe the credit. Errors:
StatuscodeCondition
400invalid_amountamount_cents below minimum or above maximum
402card_declinedBank declined the card
402authentication_required3DS required — response carries next_action: "confirm_payment" and a client_secret your front-end passes to Stripe.js
502provider_unavailableStripe upstream failure; safe to retry

Add / save a payment method

Two-step flow: create a SetupIntent, confirm it client-side, then set the resulting payment-method id as the org’s default.

1. Create a SetupIntent

curl -X POST https://api.thunderphone.com/v1/billing/setup-intent \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"
Response
{
  "setup_intent_id": "seti_abc123",
  "client_secret": "seti_abc123_secret_..."
}

2. Confirm client-side with Stripe.js

See the Stripe documentation for stripe.confirmCardSetup(). On success you receive a payment_method id.

3. Mark the payment method as default

curl -X POST https://api.thunderphone.com/v1/billing/default-payment-method \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"payment_method_id": "pm_abc123"}'
Returns 200 OK with the updated Billing account object; has_payment_method is now true.

List transactions

Returns credits (top-ups), debits (per-call billing), and adjustments.
curl 'https://api.thunderphone.com/v1/billing/transactions?limit=50' \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY"

Query parameters

ParamTypeDefaultDescription
limitinteger501–200
offsetinteger0
kindstringcredit, debit, adjustment, refund
start_dateISO 8601Filter on created_at
end_dateISO 8601

Transaction object

{
  "id": 3421,
  "kind": "debit",
  "description": "Call billing: 2 minutes × $0.08",
  "amount_cents": -16,
  "amount_usd": "-0.16",
  "call_id": 987654321,
  "phone_number_id": 201,
  "phone_number_value": "+15551234567",
  "metadata": {},
  "created_at": "2026-04-20T18:25:06.201Z"
}
amount_cents is negative for debits and positive for credits.

Stripe webhook

ThunderPhone receives Stripe webhooks at /v1/stripe/webhook internally — you don’t interact with this endpoint directly. It processes payment_intent.succeeded, payment_intent.payment_failed, and setup_intent.succeeded events to update balances and mark payment methods ready.

Outbound Calls

Outbound calls require positive balance.

Pricing

Per-minute pricing by product tier.