First month free with promo codeFirst month free with codeRedeem promoRedeem

AI Phone Call API Tutorial: Make Your First Outbound Call in 10 Minutes

On this page
  1. What an AI phone call API actually does
  2. Prerequisites and authentication
  3. Your first call: one cURL command
  4. Polling for the result: the call lifecycle
  5. A working Python loop
  6. The same loop in Node
  7. Loop-in, recordings, and IVR edge cases
  8. Skipping the tutorial: hosted MCP and the agent skill
  9. How this API compares to the alternatives
  10. Frequently asked

An AI phone call API is an HTTP endpoint that dials a real phone number, runs a realtime voice agent against the audio stream, and returns the outcome and a transcript when present after the call ends. This tutorial walks the full loop end to end with copy-paste examples in cURL, Python, and Node, using ClawCall’s REST API at api.clawcall.dev as the worked example. You will go from zero credentials to a finalized call result in about ten minutes. For MCP-compatible hosts, ClawCall’s preferred path is hosted OAuth MCP at https://api.clawcall.dev/mcp; this post covers the REST fallback for custom code and hosts without remote MCP and OAuth. It explains fire-and-poll, authentication, lifecycle states, temporary recording retrieval, live handoff to you, and the packaged-skill fallback. No credit card is required for the free trial of 30 calls and 30 minutes, whichever lasts later.

What an AI phone call API actually does

An AI phone call API is an HTTP endpoint you POST to with a destination number and a task description. The provider’s backend dials the number over the PSTN, runs a realtime voice agent against the audio stream, and hands back a structured result once the call ends. The agent handles the messy parts a normal API cannot: pressing IVR digits, waiting through hold music, repeating itself when a receptionist mishears, recognizing voicemail and following your message instructions, and bridging the call to a human when verification is needed. You are not assembling a SIP trunk, a speech-to-text pipeline, a turn-taking model, and a TTS engine yourself. You are calling a finished product.

The common use cases fall into two camps. Developers wire calls into agents and workflows: a coding agent session that calls a clinic to book an appointment, a task that disputes a duplicate utility charge, an automation that confirms restaurant reservations the night before. Consumers use the same backend through chat or a web dashboard for one-off tasks like canceling a gym membership or sitting on hold with the airline. A well-designed API serves both from the same REST contract, so anything you build composes with the rest of the product. There is no separate developer tier with different limits or model behavior.

The shape tends to be similar across vendors: a POST endpoint that returns an identifier immediately, a polling endpoint for status, and captured transcript evidence when present and temporary recording access when available at the end. What varies is how much you configure (assistants, prompts, voices, transports), how billing works (per minute or flat), and whether the call agent has guardrails around voicemail and AI disclosure.

Prerequisites and authentication

You need three things: a terminal, a US phone number you control for the first test call, and about ten minutes. You do not need a credit card, a provisioned phone number, or your own carrier, speech, or voice accounts. ClawCall manages the telephony and voice stack and exposes a single REST surface at api.clawcall.dev. The free trial covers 30 calls and 30 minutes, whichever lasts later.

Authentication has three modes:

  • Anonymous: no header at all.
  • API key: an X-Api-Key header.
  • Session token: for signed-in users of the web app.

The anonymous path is the interesting one. The first POST /call without credentials auto-issues a proto-key in the response body under the api_key field. Store it and send it as X-Api-Key on every later request. The same key keeps working after you sign up, because sign-up links it to your account. You can test the integration with zero friction and move to a paid plan later without rewriting client code or rotating credentials. Until you link it, the proto-key is limited to your IP’s free-trial allowance; after that it draws from your subscription.

For this tutorial, have your own US mobile number ready in E.164 format (for example +14155550123) and a one-line task in plain English. You will not write a script, a state machine, or a dialog tree. You tell the agent what outcome you want and any constraints it should respect.

Your first call: one cURL command

Open a terminal and run a single POST. Replace +15555550123 with your own US mobile number so the first test call comes to you. The to field accepts E.164 format and is restricted to +1 NANP today. The task field is plain English: tell the agent what outcome you want and any constraints it should respect.

Shell
curl -X POST https://api.clawcall.dev/call \
  -H 'Content-Type: application/json' \
  -d '{
    "to": "+15555550123",
    "task": "Say this is a ClawCall API test and ask about the weather."
  }'

The response returns immediately with a call_id, status set to queued, and an api_key field if this was an anonymous call. Save both. The API is fire-and-poll by design. Dialing, ringing, talking, and hangup all happen asynchronously on the telephony side, and you read the final state by polling GET /call/:id rather than holding an HTTP connection open.

This matters because realistic calls run two to fifteen minutes, and no load balancer should hold a request open that long. It also keeps client code small: one POST, a polling loop, then the result. In a serverless or background-job runtime, the asynchronous shape is the only viable model; a synchronous wait-for-the-call endpoint would time out at the edge or tie up a worker. Most production voice APIs use the same fire-and-poll contract, so the pattern ports to other vendors.

Polling for the result: the call lifecycle

Now poll GET /call/:id every few seconds until the call finishes. The response uses two fields worth learning separately.

The lifecycle field moves through queued, dialing, answered, and finalized, in that order. Finalized means the call ended for any reason and the result is stable. Treat lifecycle as the gate for whether to keep polling.

The outcome field is a separate enum that tells you what happened on the phone network:

  • Someone picked up: answered
  • The call rang out or was refused: no_answer, busy, rejected
  • The number could not be reached: invalid_number, unreachable
  • The call was stopped: cancelled
  • Something failed: network_error, system_error, failed
Shell
curl https://api.clawcall.dev/call/$CALL_ID -H "X-Api-Key: $KEY"

Once lifecycle is finalized, the response includes talk_seconds and structured timestamps. The transcript and recording_url fields are nullable: inspect captured turns only when transcript is present, and use the temporary recording URL only when recording_url is present. Poll every two to three seconds, with simple backoff after the first thirty seconds; calls that hit hold music can run long.

The split between lifecycle and outcome is worth the extra field. Designs that merge them into one status string (queued, ringing, voicemail, completed, failed) make filtering and billing harder. With a clean split, “all finalized calls last week where outcome is not answered” is one filter; with a merged enum it is a switch statement. The nullable outcome_detail.hangup_cause and outcome_detail.sip_hangup_cause fields can help explain carrier-level failures when the provider supplies them.

A working Python loop

Here is the same flow as a complete Python script. It places the call, captures the proto-key from the first response, polls until finalized, and prints transcript turns when present. The only external dependency is requests.

Python
import os, time, requests

BASE = 'https://api.clawcall.dev'
resp = requests.post(f'{BASE}/call', json={
    'to': os.environ['MY_NUMBER'],
    'task': 'Ask what the weather is like and thank them.'
}).json()

key = resp.get('api_key')
call_id = resp['call_id']
headers = {'X-Api-Key': key} if key else {}

while True:
    call = requests.get(f'{BASE}/call/{call_id}', headers=headers).json()
    if call['lifecycle'] == 'finalized':
        break
    time.sleep(3)

print(f"Outcome: {call['outcome']}, duration: {call.get('talk_seconds')}s")
for turn in call.get('transcript') or []:
    print(f"{turn['role']}: {turn['text']}")
if call.get('recording_url'):
    print(f"Temporary recording: {call['recording_url']}")

That is the entire integration. No SDK to install, no webhook server, no SIP credentials, no audio codec to negotiate. Save the proto-key returned in resp and reuse it on the next call to draw from the same usage bucket. When you sign up, the same key links to your account and the script keeps working unchanged.

For production, three small refinements are worth adding:

  • Cap the total wait (say 20 minutes) so a stuck call cannot block your worker forever.
  • Back off after the first 30 seconds (3s, 5s, 8s, then 10s capped) to cut request volume on long holds.
  • On a 429 response, read the structured error and quota details, follow any returned action, and do not retry blindly.

None of these change the shape of the integration. They just make it polite.

The same loop in Node

The Node version uses the built-in fetch in Node 18 and later, so there is nothing to install. Save it as call.mjs and run it with MY_NUMBER set to your own US number.

JavaScript
const BASE = 'https://api.clawcall.dev';

const start = await fetch(`${BASE}/call`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    to: process.env.MY_NUMBER,
    task: 'Ask what the weather is like and thank them.',
  }),
}).then((res) => res.json());

const headers = start.api_key ? { 'X-Api-Key': start.api_key } : {};
let call;
do {
  await new Promise((resolve) => setTimeout(resolve, 3000));
  call = await fetch(`${BASE}/call/${start.call_id}`, { headers }).then((res) => res.json());
} while (call.lifecycle !== 'finalized');

console.log(`Outcome: ${call.outcome}, talk time: ${call.talk_seconds}s`);
for (const turn of call.transcript ?? []) console.log(`${turn.role}: ${turn.text}`);
if (call.recording_url) console.log(`Temporary recording: ${call.recording_url}`);

The shape matches the Python loop: one POST, a polling loop on lifecycle, then the outcome. The transcript and recording_url fields are nullable, so the code reads them only when present. Store the api_key from the first response and send it as X-Api-Key on every later call, including after you sign up.

Loop-in, recordings, and IVR edge cases

Two API features matter past the hello-world stage.

The first is the loop_in_user field, which lets the AI hand the live call to you mid-conversation. Set loop_in_user to true on POST /call from an account with a verified phone, and the agent gets a loop_in_user tool. When verification, payment confirmation, or a human handoff is needed, the agent asks the other person to hold, calls your verified phone, and connects you once you accept; you finish the call yourself. This covers callees who insist on talking to a real person or ask for a card number the agent will not handle.

Older integrations that send bridge_number keep working. Every plan allows one outbound call at a time per account. Trial and Unlimited calls use shared lines. A second call while one is running returns concurrent_call_limit. Wait for the current call to finalize before placing the next. If all shared lines are busy with other accounts, number_pool_exhausted means you can retry shortly. Unlimited Reserve and Reserve Plus calls always use your reserved number, one call at a time. On shared-pool calls, looping you in uses a second shared line while connected.

The second is recording retrieval. When a recording exists, GET /call/:id/recording redirects to its temporary URL. ClawCall deletes recordings 10 minutes after a call ends, so fetch or copy the audio promptly if you need it. Transcripts are deleted 7 days after a call ends, and a call can finish without a transcript.

IVR navigation is handled by the agent; you do not write DTMF logic. The send_dtmf tool exists, but you rarely call it yourself because the agent fires it when it detects a menu prompt. When present, transcripts can include synthesized turns for IVR menus, so you can audit what the agent navigated through.

Voicemail is task-driven. The agent recognizes a voicemail greeting, leaves a concise message when your task asks for one, and otherwise reports that it reached voicemail. You do not write voicemail heuristics; captured transcript evidence, when present, shows what happened on the line, and the outcome remains a phone-network result.

Skipping the tutorial: hosted MCP and the agent skill

If you are wiring calls into an agent host like Claude Code or Cursor, you can skip most of this tutorial. ClawCall’s preferred agent connection is hosted OAuth MCP; compatible hosts sign in instead of receiving a ClawCall API key. The packaged skill and REST API are fallbacks for hosts without remote MCP and OAuth. Use the REST walkthrough here only for a custom integration or an incompatible host, where authentication, polling, and nullable transcript handling then apply.

Most readers of an API tutorial are not building a phone product. They are giving phone capability to an existing agent. Hosted OAuth MCP is the right entry point for compatible hosts. The packaged skill and REST API cover incompatible hosts and custom backends where you need explicit control over the HTTP layer, and both hit the same underlying contract.

The packaged skill is a short bootstrap. It tells the agent to fetch ClawCall’s current guides from the server before each operation and then use the documented endpoints, so API formats stay current without reinstalling. It also covers credential bookkeeping: storing the issued key, attaching it on later calls, and surfacing sign-up links when the free trial runs out. Install instructions are at /for-agents. The skill is licensed MIT-0 and the docs are CC BY 4.0. If you have already run a successful call with this tutorial, you are minutes from giving your agent the same capability.

How this API compares to the alternatives

There are three broad camps: voice infrastructure for teams building their own voice products, consumer AI call apps with thin or no APIs, and a few finished products in between.

ProductsCategoryGood fit when
Vapi, RetellVoice infrastructure, per minuteYou ship a voice product and want to tune model, voice, and prompts
Synthflow, VocodeVoice infrastructureYou want an enterprise builder (Synthflow) or open-source self-hosting (Vocode)
BlandHosted voice agents, per minuteYou want the model, speech, and voice bundled in one per-minute rate
RegalOutbound salesYou run a sales call center
Goodcall, Rosie, Numa, ReplicantInbound receptionistYou want your business line answered
AgentPhone, AgentLinePhone numbers for agents, per minuteYour agent needs its own number for inbound calls and texts
Apple Hold Assist, HoldForMe.aiHold-waitingYou only need someone to wait on hold
ClawCallFinished calling productYour agent or script needs to place calls today

Inbound receptionists are the wrong tool for outbound calls from an agent or script, though they are excellent as a 24/7 answering service. Apple Hold Assist is a built-in phone feature and HoldForMe.ai is a hold service; neither exposes the kind of API this tutorial is about. For a side-by-side with published prices, see the developer API comparison.

ClawCall’s case for an API reader is simple: a finished product with a clean REST contract, flat $4.99–$14.99 monthly pricing instead of per-minute billing, a no-card free trial of 30 calls and 30 minutes, whichever lasts later, mandatory AI disclosure, voicemail only when instructed, and hosted MCP with a packaged skill fallback. If your goal is to give an agent phone capability today, it is the shortest path. If you are building a voice product from scratch, Vapi, Retell, or Vocode are better starting points.

Frequently asked

How long does it take to make my first AI phone call with the ClawCall API?

About ten minutes from a cold start. The first POST /call request is anonymous and needs no signup, credit card, or provisioned phone number. You send one HTTP request with a destination number in E.164 format and a plain-English task, save the api_key returned in the response, and poll GET /call/:id every few seconds until lifecycle is finalized. The free trial covers 30 calls and 30 minutes, whichever lasts later. If you are working in Claude Code or Cursor, hosted MCP or the packaged skill (see for agents) wraps the whole flow and gets you to a working call faster. No SDK install is required for the raw API path.

What does the response from POST /call look like?

POST /call returns immediately with a JSON body containing call_id, status set to queued, and (on anonymous first calls) an api_key field starting with clawcall_sk_. The call result is not in this response, because real phone calls take minutes; you poll GET /call/:id for the final state. The polled response separates two concerns. Lifecycle (queued, dialing, answered, finalized) tells you whether to keep polling. Outcome (answered, no_answer, busy, rejected, invalid_number, unreachable, network_error, cancelled, system_error, or failed) tells you what happened. The final response includes nullable talk_seconds, transcript, and recording_url fields plus nullable carrier details under outcome_detail.

Do I need to handle voicemail, IVR menus, or hold time myself?

No. The voice agent handles all three internally. When the call reaches voicemail, it follows your task: leave a concise message if you asked for one, or report that voicemail was reached if you did not. IVR menus are navigated automatically using a built-in send_dtmf tool; menu prompts and pressed digits can appear in captured transcript evidence when present. Hold time is handled by the agent simply waiting; talk_seconds reflects the full duration including hold. You do not write DTMF logic, voicemail heuristics, or hold-detection code. Your client code is one POST and a polling loop, regardless of how complex the actual call ends up being on the line.

Can the AI hand the call to me when a human needs to take over?

Yes. Set loop_in_user to true on POST /call from an account with a verified phone, and the agent gets a loop_in_user tool. When the agent decides a handoff is needed (a verification step, a payment authorization, or the other person insisting on a human), it asks them to hold, calls your verified phone, and connects you once you accept; you finish the conversation yourself. Every plan allows one outbound call at a time per account. Trial and Unlimited calls use shared lines. A second call while one is running returns concurrent_call_limit. Wait for the current call to finalize before placing the next. If all shared lines are busy with other accounts, number_pool_exhausted means you can retry shortly. Unlimited Reserve and Reserve Plus calls always use your reserved number, one call at a time. On shared-pool calls, looping you in uses a second shared line while connected. The contract is in the REST API docs, and Hold For Me shows the consumer version of the pattern.

What are the rate limits and concurrency limits on the API?

The free trial allows 30 calls and 30 minutes, whichever lasts later, before sign-up is required; after that, a 429 or 402 response carries structured error and quota details with an action URL. Every plan allows one outbound call at a time per account. Trial and Unlimited calls use shared lines. A second call while one is running returns concurrent_call_limit. Wait for the current call to finalize before placing the next. If all shared lines are busy with other accounts, number_pool_exhausted means you can retry shortly. Unlimited Reserve and Reserve Plus calls always use your reserved number, one call at a time. On shared-pool calls, looping you in uses a second shared line while connected. The error for a busy reserved number is reserved_number_busy. There is no per-minute billing on any plan: $4.99 a month for Unlimited, $8.99 for Unlimited Reserve, and $14.99 for Unlimited Reserve Plus. US numbers only.

How does this compare to Vapi, Retell, or building on a raw carrier plus an LLM?

Vapi and Retell are infrastructure-grade voice platforms for teams building their own voice products end to end, with per-minute pricing and rich assistant-configuration APIs. Rolling your own on a raw carrier plus a realtime LLM is the fully assemble-it-yourself path: you control everything, and you also build the SIP plumbing, codec bridge, and turn-taking logic. ClawCall sits one level higher: a finished consumer-and-agent product exposed through a clean REST API and hosted MCP, with flat monthly pricing, a no-card free trial of 30 calls and 30 minutes, whichever lasts later, mandatory AI disclosure, and voicemail only when instructed. Pick the option that matches what you are actually building. See ClawCall vs Vapi and ClawCall vs Retell for line-by-line breakdowns.

Keep reading

Hand off the next call.

Free trial: 30 calls and 30 minutes, whichever lasts later. No credit card.