Skip to main content
Outbound calling lets you hand a destination number and an agent configuration to ThunderPhone and have the AI place the call on your behalf. Typical use cases:
  • Appointment confirmations
  • Survey callbacks
  • “Second-try” follow-ups after a missed call
  • Dispatch-style notifications

Prerequisites

1

Bring a VoIP number

Outbound calling requires you to own the from_number through a VoIP connection. Demo numbers are inbound-only. See Bring your own numbers.
2

Create an agent

An outbound-flavoured prompt tends to start with the agent identifying itself and its purpose — “Hi, this is Acme calling to confirm your appointment for tomorrow at 3pm…” Set outbound_speak_order to agent_first (the default).
3

Keep a positive balance

Outbound calls return 402 Payment Required if balance is ≤ $0.00. Top up via POST /v1/billing/top-up or enable auto-reload.

Place a call with a saved agent

The simplest path — reference an agent by id:
curl -X POST https://api.thunderphone.com/v1/call \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from_number": "+15551234567",
    "to_number":   "+14155550199",
    "agent_id":    12
  }'
Response:
{ "call_id": 987654321, "status": "initiated" }
status: "initiated" just means the request was accepted — the call is not yet connected. Poll GET /v1/calls/{call_id} for the live status (in_progresscompleted / failed).

Place a call with inline config

If you want a one-off prompt that isn’t worth saving as an agent, pass config instead. The shape matches the response schema of the call.incoming webhook:
curl -X POST https://api.thunderphone.com/v1/call \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from_number": "+15551234567",
    "to_number":   "+14155550199",
    "config": {
      "prompt":  "You are confirming Jane Doe appointment for 3pm tomorrow…",
      "voice":   "en-US-James1",
      "product": "spark"
    }
  }'

Follow the call

In parallel, subscribe to the telephony.complete webhook — the fastest way to know a call finished. If you can’t accept inbound webhooks, poll GET /v1/calls/{call_id} every couple of seconds; the record includes end_reason, duration_seconds, and the recording URL once the call ends.

Failure modes worth handling

ErrorFix
402 Payment RequiredTop up the balance or enable auto-reload
403 outbound blocked (demo number)Bring a VoIP number instead
403 outbound blocked (unverified VoIP)Run POST /v1/phone-numbers/{id}/verify-voip
404 from_number is not registered to this organizationConfirm the from_number matches a phone number you own
502 Bad GatewayTransient SIP / LiveKit failure; safe to retry

Controlling hold time

Outbound calls that run long because the callee is slow to respond (IVR trees, queues) can be capped with max_hold_seconds:
{
  "from_number": "+15551234567",
  "to_number":   "+14155550199",
  "agent_id":    12,
  "max_hold_seconds": 120
}
The agent hangs up if no human audio has been received in the last N seconds. Default is 900 (15 minutes).

Next steps

Outbound calls reference

Every request field and error code.

Receive call.complete

Stream finished outbound calls to your system.

Billing

Auto-reload so outbound never fails on balance.

Test outbound agents

Dry-run your outbound agent before production.