# AgentCall: Complete Reference ## Current offer and agent behavior New accounts receive a one-time 72-hour live trial with one US/Canada local number, 300 managed inbound AI seconds (five minutes) total, and five SMS send attempts to verified destinations. Card verification is required before number allocation; the clock starts when allocation succeeds. There is no monthly reset or automatic paid subscription. Live OTP extraction and outbound calls require Pro. Existing Free accounts retain their assigned monthly allowances. Read the account's offerVersion, trial and limits rather than assuming every Free account has the same offer. Only describe a monthly reset when the account response supplies a non-null resetsAt. Pro is $19.99/month plus number rental and usage: local/mobile numbers $2/month each, toll-free $2.50/month, outbound SMS $0.015/message, standard outbound voice $0.035/min, managed AI voice $0.40/min, BYOK voice $0.10/min plus your AI provider's charges, or Premium Voice $0.59/min. AI calls round up to whole minutes per call. New-account destination/business verification can still apply after upgrading. After trial expiry, the number is held for at least 48 hours before release, with a successful expiry notice before release becomes eligible. A delayed notice extends the hold. Trial expiry and number retention are separate; stored configuration and results remain available. Upgrading during the hold preserves the number. Before quoting allowances or taking a paid action, call get_plan (or GET /v1/account) and use the returned offerVersion, trial, limits, billingStatus and summary. New live_trial_72h_v1 accounts have a one-time 72-hour trial starting at successful number allocation, 300 managed inbound AI seconds total and five verified-destination SMS attempts; card verification is required first. No monthly reset, automatic upgrade or Free overage. Existing free_monthly_v1 accounts retain assigned monthly allowances. Live OTP extraction, outbound calls, BYOK and Premium Voice require Pro for new trial accounts. Explain payment_method_required, payment_recovery_required, trial expiry and quota errors; stop retrying them. Share the returned setup/upgrade link when the user wants to proceed, let the human complete card/payment steps, then reread get_plan before retrying. Do not assume saving a card upgrades the account, or promise a released number can be recovered. Pro is $19.99/month plus number rental and usage. Do not auto-run upgrade_to_pro just because another tool failed. Honor the user's authorization for any real-world action. Read https://api.agentcall.co/llms.txt for the current reference. > AgentCall is the phone number API built specifically for AI agents. It gives autonomous agents US and Canada phone numbers with SMS, voice calling, AI voice calls, automatic OTP extraction, Auditable Call Memory (cross-call memory for AI phone agents that remembers every caller, with audit-trail evidence quotes), saved outbound AI agents per number, two-way AI SMS, relay mode (text your own agent), standalone text-to-speech, and a 62-tool MCP server with 5 prompts. ## Overview AgentCall solves a real gap in AI agent infrastructure: agents need phone numbers to send SMS, make calls, run end-to-end tests on their own apps, and receive OTPs, but existing telecom APIs are built for human developers, not autonomous software. AgentCall provides an agent-native API and a hosted MCP server. A single API call provisions a number, and webhooks deliver every SMS and call event directly to your agent's endpoint. Numbers are routed via licensed US carriers. Website: https://agentcall.co API Base URL: https://api.agentcall.co MCP Server: https://api.agentcall.co/mcp Dashboard: https://agentcall.co/dashboard Customer support: support@agentcall.co Legal / business / privacy: contact@agentcall.co --- ## Getting Started: Four Install Paths ### Path 1: Hosted MCP Server (Recommended) No Node.js required. Works with Claude Desktop, Claude Code, Cursor, Windsurf, Hermes Agent, OpenClaw, and any MCP client that supports streamable HTTP. Paste this config into your MCP client settings: ```json { "mcpServers": { "agentcall": { "transport": "streamable-http", "url": "https://api.agentcall.co/mcp", "headers": { "Authorization": "Bearer YOUR_API_KEY" } } } } ``` Replace `YOUR_API_KEY` with your key from https://agentcall.co/api-keys. Restart your AI tool and all 62 phone tools are available immediately. ### Path 2: Smithery Registry Browse and install from Smithery, the largest MCP server registry. Listing: https://smithery.ai/server/agentcall/agentcall. ``` npm install -g smithery smithery mcp add agentcall/agentcall \ --headers '{"agentcallApiKey":"ac_live_your_key_here"}' ``` Smithery hosts its own MCP URL for the server at `https://agentcall--agentcall1.run.tools` and handles OAuth and token refresh, useful for clients that prefer that to managing the Bearer header themselves. ### Path 3: OpenClaw Environment Variables For OpenClaw agents running on a VPS (Hostinger, etc.) or locally. Set these two environment variables: ``` AGENTCALL_API_KEY=your_api_key_here AGENTCALL_API_URL=https://api.agentcall.co ``` Your agent hits the discovery endpoint at `GET https://api.agentcall.co/` and auto-learns all 62 tools, auth format, and docs URL. No manual config needed. AgentCall is also available as a skill on the ClawHub marketplace; tell your agent "Install the agentcall skill from ClawHub". ### Path 4: Local npx (Advanced) Run the MCP server locally. Requires Node.js 20+. For Claude Code (one command): ``` claude mcp add agentcall --env AGENTCALL_API_KEY=your_key -- npx -y @agentcall/mcp-server ``` For Claude Desktop / Cursor / Windsurf config: ```json { "mcpServers": { "agentcall": { "command": "npx", "args": ["-y", "@agentcall/mcp-server"], "env": { "AGENTCALL_API_KEY": "your_api_key_here" } } } } ``` For OpenAI Codex (`~/.codex/config.toml`, TOML not JSON, stdio only): ```toml [mcp_servers.agentcall] command = "npx" args = ["-y", "@agentcall/mcp-server"] [mcp_servers.agentcall.env] AGENTCALL_API_KEY = "your_api_key_here" ``` --- ## Inbound AI Voice (72-hour live trial; Pro Managed $0.40/min or BYOK $0.10/min) Provision a number, configure inbound AI with a system prompt, and incoming calls are auto-answered by an AI voice agent that follows your prompt. No webhook handler required. After every call, AgentCall summarizes the transcript with an LLM and emails a plain-English summary (caller name, phone, intent, urgency, callback time) to an address you configure on the number, so small-business owners don't have to read transcripts. Spam calls are auto-suppressed from the email. ### Owner SMS call summaries AgentCall texts the business owner a short summary of every call their AI receptionist handles, in addition to email. Turn it on per number by setting a notification phone number (dashboard, API, or MCP). One text per call (spam skipped) with the caller, what they wanted, urgency, and any callback time. US numbers only. Reply STOP to turn it off. Billed as one outbound SMS at $0.015. Email and SMS are independent, so a number can notify by email, by text, or by both. To enable the text, add an `smsTo` field (E.164, US only) to the `notify` block alongside `emailTo`. It is optional and off by default. Available on US and Canada numbers. New accounts get 300 managed inbound AI seconds total within a one-time 72-hour live trial, with card verification before number allocation. Exhaustion or expiry stops new trial calls; it does not create paid overage or a monthly reset. Existing Free accounts keep their assigned monthly offer. Read get_plan or GET /v1/account before quoting limits. ```bash # 1. Provision a number: pass an optional `areaCode` to target an NPA close # to your customers. Useful for the small-business "we're in your town" # feel (e.g. 314 for a St. Louis receptionist, 416 for Toronto). Works for # both US and CA: Canada uses the same NANP 3-digit format. Omit for any # local number in the country. curl -X POST https://api.agentcall.co/v1/numbers/provision \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "local", "country": "US", "areaCode": "314" }' # Same call for a Toronto (416) number: curl -X POST https://api.agentcall.co/v1/numbers/provision \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "local", "country": "CA", "areaCode": "416" }' # 2. Configure inbound AI on that number: and (optionally) the post-call email curl -X POST https://api.agentcall.co/v1/numbers/num_abc123/inbound-config \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mode": "ai", "systemPrompt": "You are the front desk for Acme Plumbing. Greet callers and take their name, address, and the issue.", "voice": "sage", "firstMessage": "Thanks for calling Acme Plumbing. How can I help?", "maxDurationSecs": 600, "transferTo": "+13145550100", "notify": { "emailTo": "owner@acmeplumbing.com", "smsTo": "+13145550100", "businessName": "Acme Plumbing", "agencyName": "Acme Plumbing" } }' # 3. Incoming calls to that number are now answered by the AI, and after each # call a plain-English summary email lands in owner@acmeplumbing.com, plus a # one-line summary text to +13145550100 (smsTo is optional, US only, off by # default; one outbound SMS at $0.015 per call, spam skipped, reply STOP to # turn it off). # To disable later: curl -X DELETE https://api.agentcall.co/v1/numbers/num_abc123/inbound-config \ -H "Authorization: Bearer YOUR_API_KEY" # transferTo (optional, E.164): transfer to a human. When set, the AI hands # the live call to a real person whenever the caller asks for one, or when it # cannot help. It says a short handoff line, then transfers the call; whoever # answers sees the caller's number, not yours. If nobody picks up within about # 25 seconds, the AI resumes and takes a message instead, so the caller is # never dropped. No webhook or tool setup needed; coexists with declared # tools. AI minutes stop at the handoff; the human portion of the call bills # at the standard $0.035/min voice rate. Transfers show as transferredTo / # transferredAt on the call record (GET /v1/calls/:id). Omit or pass null for # no transfer (the AI takes a message, today's default). Sizing tip: # maxDurationSecs covers the whole call including the time after the handoff, # so set it higher (up to 3600) on numbers where transferred conversations # run long. # Want the AI to answer in Spanish only? Add "language": "es" to the body. # Supported codes: auto (default, matches caller), en, es, fr, de, it, pt, nl, # ja, ko, zh, hi, ar, ru, id, tr, pl, uk, vi, ta, ms, ro, el, cs, sv, hu, da, # fi, no, sk, hr, bg (31 languages plus auto). Stored separately from # systemPrompt so swapping the language never wipes the prompt. Use PATCH # /v1/numbers/:id with body # { "language": "es" } to change just the language on an existing config. ``` Node.js SDK: ```javascript await client.numbers.configureInboundAi(numberId, { systemPrompt: 'You are the front desk for Acme Plumbing...', voice: 'sage', firstMessage: 'Thanks for calling Acme Plumbing. How can I help?', maxDurationSecs: 600, transferTo: '+13145550100', // optional: the AI hands callers to this number when they ask for a person notify: { emailTo: 'owner@acmeplumbing.com', smsTo: '+13145550100', // optional, US only, off by default. One $0.015 SMS per call. businessName: 'Acme Plumbing', agencyName: 'Acme Plumbing', }, }) // Disable await client.numbers.disableInboundAi(numberId) ``` Same `notify` block is exposed on the MCP `configure_inbound_ai` tool, so AI agents in Claude Desktop, Cursor, Windsurf, Hermes, and OpenClaw can wire up inbound AI plus the email and text destinations in a single tool call. No dashboard handoff needed. The `notify` block takes `emailTo` and an optional `smsTo` (E.164, US only, off by default); set either, or both, to control where the post-call summary lands. --- ## Two-way AI SMS (your AI answers texts on your behalf) Two-way AI SMS turns a number into an AI that answers inbound texts the way inbound AI voice answers calls. Set `smsMode: "ai"` on the same `configure_inbound_ai` endpoint and every inbound text gets an LLM reply that follows your prompt and remembers the contact across both voice and SMS, instead of just being parsed for an OTP. - **Managed-only, Pro plan.** Text replies always use AgentCall's managed model (gpt-4o-mini); there is no BYOK for SMS. Each reply is billed as one standard outbound text ($0.015), with no SMS surcharge. - **STOP is sacred.** STOP/UNSUBSCRIBE/CANCEL/END/QUIT (and START to resubscribe) are handled before the AI runs, so an opt-out is honored, never answered by the model. - **Reactive by default, proactive on demand.** The AI reply path fires when someone texts in. To start the conversation yourself, either send the first message with `POST /v1/sms/send` (`send_sms`) or set a proactive schedule with `POST /v1/numbers/:id/schedules` (`create_schedule`) so the number's agent texts first, once or on a recurring cadence. A reply to either lands on the same thread and is answered by the AI with full context, so outbound-initiated flows (appointment reminders, payment follow-ups, check-ins) round-trip end to end. - **Safety caps.** A per-sender hourly cap and a per-thread message cap bound runaway conversations, including agent-to-agent loops, without inspecting who the sender is. - **Cross-channel memory.** A phone call informs later texts and a text thread informs later calls. The same per-contact memory and pre-call context webhook (Hermes) feed SMS replies; the context webhook is fetched once per thread and cached. ### Config fields (on `configure_inbound_ai` / `POST /v1/numbers/:id/inbound-config`) - `smsMode`: `"off"` (default), `"ai"` (AgentCall's AI answers texts), or `"relay"` (AgentCall runs no AI and forwards each text to your own agent, see Relay mode below). - `smsSystemPrompt`: optional. Overrides the voice `systemPrompt` for texts; falls back to it when unset. - `actionWebhook`: optional. `{ url (https, public), signingSecret (>=16), timeoutMs (<=8000), headers? }`. The signing secret is redacted on read (`hasSigningSecret`). - `tools`: optional array (max 8). Each `{ name (^[a-z0-9_]{1,40}$), description (<=300), parameters (JSON-Schema object) }`. - `liveTranscript`: optional boolean (default false). When true, inbound AI calls on this number stream each finalized utterance as a real-time `transcript.partial` webhook (see "Live transcript streaming" under Calls). The post-call `call.transcript` is unaffected. ### The Action Bridge: real quotes and actions, not hallucinated The declared `tools` are passed to the model as function definitions. When the AI calls one, AgentCall HMAC-signs and POSTs the call to your `actionWebhook.url`: ```jsonc POST {actionWebhook.url} X-AgentCall-Signature: sha256= { "tool": "get_quote", "arguments": { "date": "2026-06-14", "bedrooms": 2 }, "context": { "channel": "sms", "numberId": "num_...", "agentId": "...", "contact": { "phone": "+1314..." }, "threadId": "smsconv_...", "callId": null } } ``` Your endpoint replies `{ "result": "$540" }` (string or small JSON object), and the AI texts the real number back instead of guessing. Calendar, CRM, and pricing stay on your side; AgentCall is the phone + SMS + memory layer. Fail-soft: on a non-2xx, timeout, or bad body the AI receives a "tool unavailable" note and apologizes gracefully rather than inventing a confirmation. SSRF-guarded (the URL must resolve to a public address). ### In-call voice tool calling (the Action Bridge on live phone calls) The same `tools` + `actionWebhook` now power tool calling during live inbound AI voice calls, not just SMS. When an inbound AI number has both configured, the voice AI can call those tools mid-conversation and speak the real result, so a receptionist checks today's real calendar availability live instead of promising a callback, and an on-air host pulls a caller's history while they are still on the line. Nothing extra to configure: declare `tools` and `actionWebhook` on the same `inbound-config` and they work on both channels. The request body is identical to SMS, with two voice-specific context fields: `context.channel` is `"voice"`, `context.callId` is the live call id, and `context.threadId` is `null`. ```jsonc POST {actionWebhook.url} X-AgentCall-Signature: sha256= X-AgentCall-Event: action.invoke { "tool": "check_availability", "arguments": { "date": "2026-06-14" }, "context": { "channel": "voice", "numberId": "num_...", "agentId": "...", "contact": { "phone": "+1314..." }, "threadId": null, "callId": "call_..." } } ``` Same fail-soft semantics as SMS: on a timeout or non-2xx the model is told the action is temporarily unavailable and continues gracefully. The per-call tool-call count appears on the call record as `toolCallCount` and in the Call Report as `report.toolCallCount`. This runs alongside the pre-call context webhook (Hermes): context is fetched before the call, tools are called during it. ```bash curl -X POST https://api.agentcall.co/v1/numbers/num_abc123/inbound-config \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mode": "ai", "systemPrompt": "You are the front desk for Acme Moving.", "smsMode": "ai", "smsSystemPrompt": "For a price, ALWAYS call get_quote and quote the real number. Never invent a price.", "actionWebhook": { "url": "https://your-host.com/agentcall/action", "signingSecret": "whsec_min16_shared_secret" }, "tools": [{ "name": "get_quote", "description": "Real move quote for a date and home size.", "parameters": { "type": "object", "properties": { "date": { "type": "string" }, "bedrooms": { "type": "integer" } }, "required": ["date", "bedrooms"] } }] }' ``` ### Reading threads - MCP: `list_sms_conversations`, `get_sms_conversation`. - REST: `GET /v1/sms-conversations` (paginated), `GET /v1/sms-conversations/:id` (thread + last 50 messages + a `context` object classifying them by freshness). - Webhook: subscribe to `sms.ai_reply` to get each AI reply (inbound + reply message ids, the reply text, tool-call count). **Recency: use `context`, not the raw message list.** The thread detail returns `context` alongside `messages`, splitting the same thread into `recentMessages` (last 7 days), `recentSubstantiveMessages` (that list with plain greetings removed), and `olderMessages`, each item carrying its `createdAt`, `ageDays`, and `isGreeting`, plus a `freshness` block naming the window and the reference time. `messages` is the last 50 texts, which on a quiet thread can span months, so an agent that treats them as a recent conversation will describe a months-old exchange as something the user just talked about. Answer "what did we recently discuss?" from `recentSubstantiveMessages` alone; when it is empty, say there has been no recent substantive text discussion rather than reaching into `olderMessages`, durable memory, or call history and presenting any of them as a recent text conversation. --- ## Business Verification: texting people who have not contacted you first A new account cannot immediately text or call an arbitrary list. This is the one piece of onboarding friction in the product and it is worth understanding before you build against it. **What a restricted account can already do.** Contact any number it owns (so agent-to-agent testing works on day one), reply to anyone who contacted it first within the last 30 days (so inbound threads, callbacks, and two-way AI SMS all work untouched), and contact any number verified individually by having a 6-digit code delivered there and read back (25 such destinations on Pro, 5 on Free). Every account additionally gets an SMS starter allowance, sized by plan: Free can text up to 10 different new numbers, lifetime (content-screened, inside the 10-texts-per-month Free cap), and a new Pro account can text up to 25 different new numbers while it is inside its first-week restricted window (also content-screened; after 7 days Pro is unrestricted and the allowance stops mattering). Follow-ups to those same numbers never consume the allowance. Past the allowance a new-stranger text returns `403 sms_starter_exhausted`; anything else blocked returns `403 destination_not_verified`. **Why it exists.** A brand-new account texting a list of strangers is behaviourally identical to a scam operation, and paying for a plan does not separate the two. This control exists because of a real 2026 incident. It is not a soft warning; it is enforced on every outbound path including scheduled sends. **Why this is still faster than the alternative.** Sending A2P text to US numbers requires 10DLC brand and campaign registration with The Campaign Registry. On most platforms you carry that yourself and wait days to weeks for approval before your first message goes anywhere. AgentCall registers your number on its campaign for you, so that wait disappears. The verification below replaces it and clears in minutes. **The fast path (Pro).** ```bash curl -X POST https://api.agentcall.co/v1/business-verification \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "legalName": "Ridgeline Painting LLC", "website": "https://ridgelinepainting.com", "contactName": "Dana Whitfield", "useCase": "Payment reminders to existing customers with unpaid invoices, including a pay link.", "sampleMessage": "Invoice 4021 for $840 is 12 days past due. Pay here: example.com/pay/4021. Reply STOP to opt out." }' ``` Provide `website` or `ein`; either identifies the business. These are the same fields TCR asks for during 10DLC brand registration, so if you have registered a campaign before you already have them. The restriction lifts on submission, not on approval: your agent can start contacting new people right away, and a person reads the submission afterwards. Dashboard equivalent: the Verification page. **Trust states.** `restricted` (the default above), `pilot` (business verified, may reach strangers subject to a daily cap on distinct new recipients that starts at 25 on day one and rises through the first week), `standard` (no destination gate, automatic after 7 days on Pro). `GET /v1/business-verification` returns `grantsOutboundPilot`, the direct answer to whether the account may contact strangers right now. `GET /v1/verified-destinations` returns the current `trustState`. **Free plan.** Verification is Pro-only. Free accounts keep the owned / prior-inbound / 5-individually-verified routes permanently. **What never changes.** STOP, UNSUBSCRIBE, CANCEL, END, and QUIT are honored before any AI or relay logic runs, on every path, at every trust level, and an opted-out contact is skipped on every future send including scheduled ones. --- ## Relay mode: text your own agent on a real number New accounts get 300 managed inbound AI seconds total within a one-time 72-hour live trial, with card verification before number allocation. Exhaustion or expiry stops new trial calls; it does not create paid overage or a monthly reset. Existing Free accounts keep their assigned monthly offer. Read get_plan or GET /v1/account before quoting limits. New live trials include five SMS send attempts total to verified destinations during the trial. Failed or ambiguous sends consume an attempt. Existing Free accounts keep their assigned monthly SMS allowance; read get_plan or GET /v1/account. Pro outbound SMS is $0.015/message, subject to account and destination restrictions. - **AgentCall is the pipe, your agent is the brain.** AgentCall handles the phone number, threading, STOP/opt-out, and delivery. Your agent does its own thinking and tool calls (email, calendar, social, whatever it can reach). - **Async reply, no timeout to fight.** A real "check my email" turn can take a while. AgentCall pushes the text and your agent replies on its own schedule via the reply endpoint, so a slow turn never times out. - **Answers only its owner.** Set `allowedSenders` (E.164, up to 20) and the number forwards texts only from those numbers; everyone else is silently dropped, so a personal agent never answers a stranger. Leave it empty to relay from anyone. This is a sender-ID gate, not cryptographic auth, so pair it with an in-prompt passphrase for high-value agents. - **Billed as plain texts, no relay fee.** The inbound text and your agent's outbound reply bill as ordinary messages. AgentCall adds no LLM charge because it runs no LLM. - **STOP is sacred** here too, handled before any relay, and the reply endpoint refuses to text anyone who has opted out. - **You do not have to build the webhook.** One command installs the open-source relay on the machine your agent already runs on (see below). All you write is a script that takes the text on stdin and prints your agent's reply on stdout. - **Full walkthrough**: https://agentcall.co/blog/text-your-own-ai-agent-sms covers the one-call setup, the webhook payload, the async reply, and the relay-vs-two-way-AI-SMS decision. ### One-command install (the open-source relay) Most agents run on a laptop or a VPS with no public HTTPS endpoint, so "point a webhook at your agent" used to mean writing a public endpoint, hosting it somewhere reachable, and then writing the service that drains it and replies. Both halves are open source at https://github.com/Kintupercy/agentcall-hermes-bridge (MIT): a Cloudflare Worker that gives you the always-on HTTPS endpoint plus a queue, and a standard-library Python consumer that runs beside your agent. ```bash # On the machine your agent runs on: git clone https://github.com/Kintupercy/agentcall-hermes-bridge.git cd agentcall-hermes-bridge && npm install && npx wrangler login ./bootstrap.sh --install-consumer --number-id num_xxx --allow +15551234567 ``` It asks for your bridge URL, your AgentCall API key, and the phone number allowed to reach the agent, then installs a service that restarts forever (systemd system scope as root, user scope otherwise; a Dockerfile and compose file ship too), writes secrets to a 0600 file, and runs a preflight check. - **The one thing you write** is the "brain": a command that receives `{message, conversation, context, history}` as JSON on stdin and prints the reply text on stdout. Exit 0 to reply, exit 64 to deliberately say nothing, any other exit means failure and the text is retried. An HTTP mode (POST the same JSON to a local endpoint, expect `{"reply": "..."}`) is supported for agents that are already servers. - **`configure-number`** puts a number into relay mode pointing at your bridge without clobbering its voice config (it reads the current inbound config and merges). - **Three levels of verification**: `preflight` checks config, bridge, push key, API key, and your brain while sending nothing; `selftest` signs a synthetic text and pushes it through the real loop, covering every hop except the carrier without texting anyone; `verify` is the live test where you text the number and it watches the reply go out and reports the latency. - **Texts are never lost in transit.** Each pulled text is claimed for 300s rather than deleted, and acknowledged only after your agent's reply has actually been sent. A crash, a restart, or an agent that errors mid thought means the text comes back rather than disappearing. Replies carry the inbound message id as an idempotency key, so a redelivery can never double-text a human. - **`skill/SKILL.md`** in the repo is the whole procedure written for an AI agent to follow, so "connect this AgentCall number to you for two-way SMS" is a prompt rather than a project. - **Security**: a number in relay mode lets a text message attempt anything your agent can do. If your agent can run commands, spend money, or read files, decide deliberately whether those tools belong on this channel, and put a narrower agent on SMS if not. `allowedSenders` helps, but caller ID is a claim rather than a credential. Full threat model in the repo's consumer README. ### Config fields (on `configure_inbound_ai`) - `smsMode`: `"relay"`. - `agentWebhook`: `{ url (https, public), signingSecret (>=16), timeoutMs (<=8000), headers? }`. Where AgentCall pushes each inbound text. Same validation, HMAC signing, SSRF guard, and secret redaction (`hasSigningSecret`) as `actionWebhook`. - `allowedSenders`: optional array of E.164 numbers (max 20). When set and non-empty, only these numbers can reach the agent. Not a secret, so not redacted. ### The relay push (AgentCall -> your agent), HMAC-signed ```jsonc POST {agentWebhook.url} X-AgentCall-Signature: sha256= X-AgentCall-Event: sms.relay X-AgentCall-Delivery: { "message": { "id": "...", "from": "+1314...", "to": "+1314...", "body": "what's critical this week?", "receivedAt": "2026-06-06T18:00:00.000Z" }, "conversation": { "id": "smsconv_...", "contactPhone": "+1314..." }, "context": { "channel": "sms", "numberId": "num_...", "agentId": "..." } } ``` ### Your agent replies when ready ```bash # 1) Point the number at your agent (no AgentCall LLM runs). curl -X POST https://api.agentcall.co/v1/numbers/num_abc123/inbound-config \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "smsMode": "relay", "agentWebhook": { "url": "https://your-agent.com/agentcall/relay", "signingSecret": "whsec_min16_shared_secret" }, "allowedSenders": ["+13145551234"] }' # 2) Your agent thinks (its own tools), then replies into the thread: curl -X POST https://api.agentcall.co/v1/sms-conversations/CONVERSATION_ID/reply \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "body": "3 emails need you today. The lease renewal is the urgent one.", "idempotencyKey": "turn_8842" }' ``` - **Reply endpoint:** `POST /v1/sms-conversations/:id/reply` with `{ body, idempotencyKey? }`. Agent-scoped (404 on a thread you do not own), opt-out-checked (403 if the recipient sent STOP), and idempotent on `idempotencyKey` for 24h so a crashed or retried agent never double-texts. Returns the sent message. - **MCP:** an MCP-connected agent can reply with the `reply_to_sms_conversation` tool instead of the REST call. - **Thread context in the push:** the relay POST carries an additive `smsContext` field beside `message`, `conversation`, and `context`. It is the thread already split by age (`currentMessage`, `recentMessages`, `recentSubstantiveMessages`, `olderMessages`, `freshness`), so your agent gets the recency split without a second API call and never has to treat the last N texts as a recent conversation. Endpoints written against the earlier payload keep working unchanged. --- ## Premium Voice Premium Voice is an opt-in upgrade for Pro that answers inbound calls with a more natural, brandable voice. It is the higher-quality voice tier that sits alongside the 10 standard voices, so you choose the right voice per number. - **What it is**: A curated library of 12 premium voices spanning warm, authoritative, and conversational styles, with female and male options and a range of accents. Each voice has a click-to-hear audio sample in the dashboard. - **Languages**: Premium Voice covers the same 31 languages as the standard AI voice, plus auto-detect. Pin a language per number or let it match the caller; no language surcharge on either tier. - **Memory**: Full Auditable Call Memory on premium calls. Cross-call memory, post-call Call Reports, and pre-call context webhooks all work identically to the standard voice. The richer voice costs you none of the smarts. - **Pricing**: $0.59/min, billed only on numbers where Premium Voice is enabled. The 10 standard voices stay the default at $0.40/min. Premium Voice is a Pro add-on (requires the $19.99/mo Pro plan and a card on file). - **How to enable (self-serve)**: From the dashboard, open Numbers, open a number's inbound AI settings, find the Premium Voice section, preview the voices, and pick one. Enabling it provisions the number for premium automatically. Switch back to a standard voice any time from the same screen. - **Enable programmatically (SDK + MCP)**: You don't have to use the dashboard. In code, call `client.numbers.setPremiumVoice(numberId, voiceId)` (Node SDK) to enable or change the premium voice, and `client.numbers.disablePremiumVoice(numberId)` to revert. AI agents can do the same via the MCP tools `set_premium_voice`, `disable_premium_voice`, and `list_premium_voices`. The number must already have inbound AI configured, and the account must be Pro with a card on file (otherwise the call returns a clear 403/402/409). It is per-number and idempotent. - **Catalog API**: Preview the premium voice library at `GET https://api.agentcall.co/v1/calls/premium-voices` (public, no auth). Each entry returns id, name, description, gender, accent, and an MP3 sample URL. - **When to use it**: Standard voices answer in about a second and suit high-volume support lines. Premium Voice trades a little response time for a richer, brandable voice, best for founder lines, sales, concierge, and brand-forward receptionists. - Announcement: https://agentcall.co/blog/premium-voice-elevenlabs --- ## Hermes Integration: AI Agent with a Real Phone Number Full guide: https://agentcall.co/docs/hermes **Who this is for:** anyone running a self-hosted Hermes agent (Nous Research, https://hermes-agent.nousresearch.com) and wanting to talk to their AI agent by phone instead of only over Telegram. Same pattern works for any agent platform that can push HTTP and listen to webhooks (n8n, custom Python jobs, OpenClaw, etc.). **What you get:** a real phone number you can call from anywhere. The AI answers in a natural voice, speaks with your latest priorities and email updates loaded from your agent's context, and the conversation flows back into your agent's memory after you hang up. You read the brief in the morning, talk through priorities in the car, and your agent handles the execution. **Architecture (what the customer owns vs what AgentCall handles):** - You own: your Hermes (or other) agent on a VPS or local machine, a tiny Cloudflare Worker bridge (open-source template at https://github.com/Kintupercy/agentcall-hermes-bridge), a webhook endpoint that receives post-call transcripts. - AgentCall handles: the phone number, carrier routing, AI voice, transcripts, recording, billing. **How the loop works:** 1. Your agent reads your inbox on a schedule (IMAP, any provider: Gmail, Outlook, ProtonMail, Fastmail, iCloud, self-hosted) 2. Generates a daily brief 3. Emails it to you AND POSTs it to your Cloudflare Worker bridge (it stores the latest brief in Cloudflare KV) 4. You call your AgentCall number 5. AgentCall hits your bridge with an HMAC-signed POST and gets back `{contextBlock}` containing the brief 6. AgentCall merges that brief onto the system prompt before the AI voice agent picks up 7. You and the AI talk through priorities. AI knows everything in today's brief. 8. Call ends. AgentCall fires the `call.transcript` webhook to your endpoint. Your agent ingests the transcript + LLM summary, extracts action items, and acts (SMTP to draft emails, calendar invite creation, memory updates). **Setup (high level, full walkthrough at /docs/hermes):** 1. Connect AgentCall MCP to your Hermes instance (one config change: add `https://api.agentcall.co/mcp` with your `ac_live_...` key as an MCP server in your Hermes config). 2. Restart Hermes. Tools auto-discover. 3. Tell Hermes via Telegram (or whatever messaging you use): "Provision a US phone number with area code 314 and label it Laura." 4. Tell Hermes: "Configure inbound AI on the new number. Voice marin. First message: 'Hey, what do you want to talk through?'. System prompt: [your agent persona]." 5. (Optional, for live context) Deploy the bridge template, set two Worker secrets, point your number's `contextWebhook` at the bridge URL, and tell Hermes to also POST briefs to the bridge. 6. (Optional) Subscribe to `call.transcript` webhook so Hermes ingests recaps automatically. **Bridge endpoints** (open-source Cloudflare Worker, ~120 LOC): - `POST /agentcall/precall`. AgentCall hits this. HMAC-verifies signature. Returns `{contextBlock}` from KV. - `POST /hermes/push`. Your agent hits this on a schedule. Auth via `X-Hermes-Push-Key` header. Stores the brief in KV. - `GET /healthz`. Liveness probe. **Email coverage:** any IMAP/SMTP provider works. Hermes is provider-agnostic. The bridge doesn't care which email service is wired up on the Hermes side; it just receives a `contextBlock` string and serves it. **Why a bridge instead of direct integration:** Hermes typically runs on your infrastructure without a public HTTPS endpoint, so AgentCall can't hit it directly during a call. The bridge is always-on, Cloudflare-hosted, and decouples your agent's runtime from the inbound call path. You own the bridge and the secrets; AgentCall never sees your agent. New accounts receive a one-time 72-hour live trial with one US/Canada local number, 300 managed inbound AI seconds (five minutes) total, and five SMS send attempts to verified destinations. Card verification is required before number allocation; the clock starts when allocation succeeds. There is no monthly reset or automatic paid subscription. Live OTP extraction and outbound calls require Pro. Existing Free accounts retain their assigned monthly allowances. Read the account's offerVersion, trial and limits rather than assuming every Free account has the same offer. Only describe a monthly reset when the account response supplies a non-null resetsAt. --- ## Auditable Call Memory Full reference: https://agentcall.co/docs/memory Product overview: https://agentcall.co/cross-call-memory Auditable Call Memory is cross-call memory for AI phone agents that remembers every caller, with audit-trail evidence quotes. Three outcomes you get the moment it's enabled: - **Every AI call produces a structured Call Report**: intent, action items, sentiment, plus source-backed memory entries. Fetch via `get_call_report` or subscribe to the `call.report.ready` webhook. - **Returning callers get personalized context automatically**: the AI knows their name, recent topics, and outstanding commitments. Fetched via `get_next_call_context` or merged on top of your system prompt at call time when the number's `contextSource` is set to `agentcall_memory` or `merge`. - **Source-backed evidence on every memory entry**: each memory line quotes the transcript span that produced it, so you can audit why the AI "remembers" something. Edit or delete any entry with `update_memory` / `delete_memory`. Briefs in the dashboard inbox flag entries that need human review. ### contextSource modes Each number can pick how pre-call context is assembled: - **`none`**: nothing injected. The system prompt is used as-is. - **`agentcall_memory`**: AgentCall's stored memory for the caller is injected on top of the system prompt. Zero customer infrastructure required. - **`webhook`**: AgentCall hits your `contextWebhook` (e.g. the Hermes bridge) and uses only your response. - **`merge`**: AgentCall first injects its stored memory for the caller, then appends your webhook's response. Use this when you want the receptionist to know returning callers AND get a fresh daily brief from your agent platform. ### Customer toggle A Cross-call memory toggle on the dashboard controls whether extraction runs. ON by default for both Free and Pro. Off means no extraction runs and no new memory entries are stored, regardless of plan. Toggle via the dashboard at https://agentcall.co/dashboard or programmatically via the `set_memory_enabled` MCP tool. ### Webhook event: `call.report.ready` After Auditable Call Memory extraction finishes for a completed AI call, AgentCall POSTs the structured Call Report to subscribed endpoints. HMAC-SHA256 signed via `X-AgentCall-Signature`, retried with exponential backoff on non-2xx. Use `callId` as the dedup key. Subscribe by adding `call.report.ready` to your webhook's `events` array. ### Dashboard surfaces - **Contacts**: https://agentcall.co/contacts. Every caller, name, last call, full memory timeline. - **Inbox**: https://agentcall.co/inbox. Briefs surfaced for human review (commitments, sensitive memory entries, conflicting facts). ### Pricing - **New live trial**: New accounts receive a one-time 72-hour live trial with one US/Canada local number, 300 managed inbound AI seconds (five minutes) total, and five SMS send attempts to verified destinations. Card verification is required before number allocation; the clock starts when allocation succeeds. There is no monthly reset or automatic paid subscription. Live OTP extraction and outbound calls require Pro. Existing Free accounts retain their assigned monthly allowances. Read the account's offerVersion, trial and limits rather than assuming every Free account has the same offer. Only describe a monthly reset when the account response supplies a non-null resetsAt. - **Pro**: Cross-call memory is included with no separate charge on top of the per-minute AI voice rate. --- ## BYOK Voice Billing (Bring Your Own OpenAI Key) Launched 2026-05-18. Every Pro number can run in one of two voice billing modes: - **Managed (default)**: $0.40/min. AgentCall handles the OpenAI API key. You pay AgentCall for the full per-minute rate. Zero setup. - **BYOK**: $0.10/min to AgentCall (for telephony, transcripts, Memory, and the AI orchestration), plus you pay OpenAI directly for model usage on your own API key. Net cost is typically lower than Managed for most workloads. Mode is selected per number. Swap any time. Both modes share the same telephony path, transcripts, post-call webhooks, and persistent caller Memory. ### How BYOK works 1. Create a standard OpenAI API key at https://platform.openai.com/api-keys with permission to use Realtime voice models. 2. Validate the key against AgentCall's tester so you know it works before attaching: `POST /v1/numbers/:id/byok/test`. 3. Attach the key to a number: `POST /v1/numbers/:id/byok/key`. The number flips to BYOK mode and bills at $0.10/min from the next call onward. 4. Inbound or outbound AI calls on that number now use your OpenAI key for the model and AgentCall for everything else. 5. Detach any time to revert to Managed: `DELETE /v1/numbers/:id/byok/key`. Keys are encrypted at rest. Only the number's own runtime can decrypt and use them; they're never returned in API responses or surfaced to other accounts. ### The 3 endpoints ```bash # 1. Validate a key works before attaching. Returns { ok, modelsAvailable, error? } curl -X POST https://api.agentcall.co/v1/numbers/num_abc123/byok/test \ -H "Authorization: Bearer YOUR_AGENTCALL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "openaiApiKey": "sk-proj-..." }' # 2. Attach the key to a number (flips number to BYOK mode, $0.10/min from next call) curl -X POST https://api.agentcall.co/v1/numbers/num_abc123/byok/key \ -H "Authorization: Bearer YOUR_AGENTCALL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "openaiApiKey": "sk-proj-..." }' # 3. Detach the key (number reverts to Managed mode, $0.40/min) curl -X DELETE https://api.agentcall.co/v1/numbers/num_abc123/byok/key \ -H "Authorization: Bearer YOUR_AGENTCALL_API_KEY" ``` ### Memory parity: identical under Managed and BYOK Auditable Call Memory works the same way regardless of voice billing mode: - **Transcript pipeline**: every AI call still produces a full transcript, identical schema under both modes. - **Extractor**: the LLM that turns the transcript into a structured Call Report and source-backed memory entries runs on AgentCall's infrastructure (not your OpenAI key). Costs are absorbed into the per-minute rate. - **Pre-call context injection**: returning callers get the same merged context block (name, recent topics, commitments) regardless of mode. The `contextSource` setting on the number is unchanged. - **Webhooks**: `call.transcript` and `call.report.ready` fire identically under both modes. If a customer asks "Do I lose Memory if I switch to BYOK?": no. Memory is foundational to AgentCall, not a Managed-only feature. ### Cost comparison vs other voice-AI platforms BYOK at $0.10/min is the lowest per-minute platform fee in the agent-voice category, while still including persistent caller Memory that other platforms don't offer: - **AgentCall Managed**: $0.40/min all-in. AgentCall handles the OpenAI key. Includes Memory. - **AgentCall BYOK**: $0.10/min to AgentCall + you pay OpenAI directly. Includes Memory. - **Vapi**: ~$0.05/min platform fee, but you also pay separately for telephony, the LLM, the TTS provider, and the STT provider. No built-in persistent caller memory. - **Retell**: ~$0.08 to $0.15/min depending on voice and model tier. No built-in persistent caller memory. If you only ever want the cheapest possible per-minute number on paper, Vapi's platform fee is lower than AgentCall BYOK. If you want persistent caller Memory bundled in, a 62-tool MCP server, and Free-tier inbound AI without a card, AgentCall is the only option in the category. ### What's NOT supported (be honest, save support tickets) - **No Anthropic, Gemini, or Grok keys**. The voice runtime is built around OpenAI's Realtime voice API. Other providers don't expose a comparable streaming voice protocol today. When they do, support will follow. - **No OAuth-based ChatGPT Plus or SuperGrok subscriptions**. BYOK requires a standard API key tied to your OpenAI platform account, billed by usage on platform.openai.com. ChatGPT Plus and SuperGrok use a different auth and billing system that can't drive an API call. - **OpenAI Realtime model only**. You can't substitute a different OpenAI model on the BYOK path; the underlying transport requires Realtime. - **No partial Managed/BYOK mix on the same number**. A number is either Managed or BYOK at any given moment. Swap any time, but a single call uses one mode end-to-end. ### REST endpoints - `POST /v1/numbers/:numberId/byok/test`. Body: `{ "openaiApiKey": "sk-proj-..." }`. Validates the key against OpenAI without attaching. Returns `{ ok: boolean, modelsAvailable?: string[], error?: string }`. Use this before `byok/key` so you can surface a clear error to the user instead of failing on first call. - `POST /v1/numbers/:numberId/byok/key`. Body: `{ "openaiApiKey": "sk-proj-..." }`. Attaches the key. The number switches to BYOK mode ($0.10/min) on the next call. - `DELETE /v1/numbers/:numberId/byok/key`. Detaches the key. The number reverts to Managed mode ($0.40/min) on the next call. --- ## Saved Outbound AI Agents Per Number Each phone number can store a reusable outbound AI agent: a systemPrompt, voice, language, firstMessage, maxDurationSecs, record flag, and optional templateId. Configure +1-555-100-0001 once as an "appointment-booker" persona, and every Place AI call from that number's row in the dashboard pre-loads it. Dialing 50 different recipients with the same persona becomes 50 single clicks instead of 50 retypes. Pro plan only (outbound AI itself is Pro). Mental model: each number is a phone identity for an AI employee. Want a second persona? Use a second number. The saved outbound agent is independent of the inbound AI receptionist on the same number; they don't touch each other. Follow-up calls launched from a specific call's detail page still take precedence over the saved agent (the stitched follow-up prompt is more specific). ### What gets surfaced where - **Dashboard**: the Place AI call dialog has Save, Update, and Clear buttons. Hitting Save stores the current dialog state on the number. Next time you click Place AI call from that number's row, the dialog hydrates from the saved blob and only the recipient field is empty. - **List/get responses**: every `GET /v1/numbers` and `GET /v1/numbers/:id` response now includes an `outbound` field alongside the existing `inbound` field. Same shape as what was saved, or `null` if none is set. - **REST**: three endpoints described below. - **SDK**: `setOutboundDefaults` / `getOutboundDefaults` / `clearOutboundDefaults` on the numbers resource (agentcall@0.6.0 and above). - **MCP**: `set_outbound_defaults` / `get_outbound_defaults` / `clear_outbound_defaults` (@agentcall/mcp-server@0.9.0 and above). ### The 3 endpoints ```bash # 1. Save (or replace) the outbound AI agent on a number curl -X POST https://api.agentcall.co/v1/numbers/num_abc123/outbound-defaults \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "systemPrompt": "You are calling on behalf of Acme Plumbing to book an appointment. Confirm the address, the preferred 2-hour window, and the issue description. Promise a follow-up email with the confirmation.", "voice": "sage", "language": "en", "firstMessage": "Hi, this is Acme Plumbing calling to book your appointment. Is now a good time?", "maxDurationSecs": 600, "record": true, "templateId": "appointment-booker" }' # 2. Read the saved outbound agent on a number (also surfaced as `outbound` on list/get) curl -X GET https://api.agentcall.co/v1/numbers/num_abc123/outbound-defaults \ -H "Authorization: Bearer YOUR_API_KEY" # 3. Clear the saved outbound agent (the dashboard dialog opens blank again next time) curl -X DELETE https://api.agentcall.co/v1/numbers/num_abc123/outbound-defaults \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Node.js SDK ```javascript import AgentCall from 'agentcall' const ac = new AgentCall('ac_live_xxxxxxxx') // Save a reusable outbound agent on a number await ac.numbers.setOutboundDefaults('num_abc123', { systemPrompt: 'You are calling on behalf of Acme Plumbing to book an appointment...', voice: 'sage', language: 'en', firstMessage: 'Hi, this is Acme Plumbing calling to book your appointment. Is now a good time?', maxDurationSecs: 600, record: true, templateId: 'appointment-booker', }) // Read it back (also available as `number.outbound` on getNumber/listNumbers) const saved = await ac.numbers.getOutboundDefaults('num_abc123') // Place 50 AI calls with the same persona: only the recipient changes for (const recipient of recipientPhones) { await ac.calls.ai({ from: 'num_abc123', to: recipient, systemPrompt: saved.systemPrompt, voice: saved.voice, language: saved.language, firstMessage: saved.firstMessage, maxDurationSecs: saved.maxDurationSecs, }) } // Clear later (returns the number's dialog to blank) await ac.numbers.clearOutboundDefaults('num_abc123') ``` ### MCP ``` set_outbound_defaults numberId=num_abc123 systemPrompt="..." voice=sage language=en get_outbound_defaults numberId=num_abc123 clear_outbound_defaults numberId=num_abc123 ``` Same fields as the REST body. Available in @agentcall/mcp-server@0.9.0 and the hosted MCP at `https://api.agentcall.co/mcp`. ### Who it's for - **Outbound sales / appointment-booking agents** that dial dozens of recipients per day with the same persona. One save, then every Place AI call is a one-click dial. - **Multi-persona setups** that use one number per persona. Configure +1-555-100-0001 as a sales agent and +1-555-100-0002 as a support callback agent; each number remembers its own outbound agent independently. - **Builders who want the same configuration available across surfaces**: dashboard hydration, list/get responses, REST, SDK, and MCP all read the same saved blob. ### What's NOT changed - **Inbound AI receptionist on the same number is untouched.** Saving an outbound agent doesn't read or write the inbound config, and vice versa. - **Follow-up calls from `/calls/[id]` still take precedence.** When you click "Follow up with AI" on a specific call, AgentCall stitches the saved-report context onto the prompt and uses that for the follow-up. The saved outbound agent is only the default for fresh Place AI calls launched from the number's row. - **One-off outbound calls don't have to save anything.** If you fill the dialog and click Place call without Save, nothing is persisted. Saved agents are opt-in. ### CSV-Driven Outbound Calls (Hermes pattern) Once a number has a saved outbound agent, you can loop through a CSV and place one AI call per row with a single HTTP request each. `POST /v1/calls/ai` accepts two flags that make this safe and ergonomic: - **`useSavedAgent: true`** hydrates any AI-config field you omit (`systemPrompt`, `voice`, `language`, `firstMessage`, `maxDurationSecs`, `record`) from the saved outbound agent on the from-number. Per-call overrides win, so you can pass a personalized `firstMessage` per recipient while inheriting the saved prompt, voice, and language. If the from-number has no saved agent, the call returns HTTP 400 with code `no_saved_agent`. - **`idempotencyKey`** (string, max 200 chars) makes retries safe. The unique scope is `(from, idempotencyKey)`. A duplicate request: - Replays the original 201 response body. - Adds response header `X-AgentCall-Idempotency-Replayed: true`. - Does NOT place a second carrier call. No double-dial, no double-charge. - Is race-safe at the database layer (unique index, P2002 catch in the handler). Canonical row call. Save the outbound agent on the from-number once, then every per-row request is this small: ```bash curl -X POST https://api.agentcall.co/v1/calls/ai \ -H "Authorization: Bearer ac_live_xxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "from": "num_abc123", "to": "+15551234567", "useSavedAgent": true, "idempotencyKey": "batch-2026-05-25:row-42", "firstMessage": "Hi Dr. Smith, this is Acme Dental confirming your appointment." }' ``` Loop over a CSV in Python. The `batch_id:row_id` idempotency-key shape means rerunning the script after a partial failure resumes safely. The `time.sleep` keeps the dial rate polite to recipients: ```python import csv, time, requests BATCH_ID = "batch-2026-05-25" FROM_NUM = "num_abc123" API_KEY = "ac_live_xxxxxxxxxxxx" with open("recipients.csv") as f: for row in csv.DictReader(f): r = requests.post( "https://api.agentcall.co/v1/calls/ai", headers={"Authorization": f"Bearer {API_KEY}"}, json={ "from": FROM_NUM, "to": row["phone"], "useSavedAgent": True, "idempotencyKey": f"{BATCH_ID}:{row['id']}", "firstMessage": f"Hi {row['name']}, this is Acme Dental.", }, timeout=15, ) replayed = r.headers.get("X-AgentCall-Idempotency-Replayed") == "true" print(row["id"], r.status_code, "replayed" if replayed else "new") time.sleep(3) ``` The SDK exposes the same two flags via `initiateAI` (agentcall@0.7.0 and above). MCP's `initiate_ai_call` tool also accepts `useSavedAgent` and `idempotencyKey` as input fields (@agentcall/mcp-server@0.10.0 and above). What this pattern does NOT do: schedule calls for later, track campaign-level completion, or auto-retry failed carriers. It is a thin, safe primitive. If your script needs scheduled queueing, run it from a worker (cron, a queue, or your agent platform) and handle the loop there. --- ## Writing System Prompts for AI Voice Calls Full guide: https://agentcall.co/docs/voice-prompts The `systemPrompt` IS the AI's brain for the call. AgentCall doesn't train or fine-tune anything. Whatever facts the AI cites, it cites from the prompt. A vague prompt produces vague (or hallucinated) answers; a structured prompt produces accurate, on-brand replies. Hard cap: 10,000 characters. ### The 4-section structure Every reliable voice-AI prompt has the same four sections. Skip any of them and the AI fills the gap by inventing details. 1. **WHAT WE DO:** facts the AI is allowed to cite. Products, services, company background. The AI never goes beyond what's in here. 2. **PRICING:** exact numbers. If the AI doesn't see a real price here, it will invent "tiered plans" or "pay-as-you-go" pricing on its own. Real example: a live test call had the AI quote tiered plans because the prompt didn't include pricing. The fix is putting the actual numbers in this section. 3. **INSTRUCTIONS:** call-flow guardrails. Tone, what to do when the AI doesn't know something, how to end the call. Always include "If asked something you don't know, say [explicit fallback]" or the AI will improvise. 4. **NEVER:** explicit prohibitions. "Never invent pricing." "Never promise outcomes." "Never stay on the line after the caller says goodbye." Last line of defense against hallucination. ### Worked example: AgentCall front desk ``` You are the AI receptionist for AgentCall, a phone-number API built for AI agents. WHAT WE DO: - Provision US and Canada phone numbers, routed via licensed carriers. - Send and receive SMS, including automatic OTP code extraction. - Make outbound voice calls and answer inbound calls, including AI voice calls where an AI handles the conversation. - Hosted MCP server at api.agentcall.co/mcp for Claude Desktop, Cursor, and Windsurf. PRICING: New accounts receive a one-time 72-hour live trial with one US/Canada local number, 300 managed inbound AI seconds (five minutes) total, and five SMS send attempts to verified destinations. Card verification is required before number allocation; the clock starts when allocation succeeds. There is no monthly reset or automatic paid subscription. Live OTP extraction and outbound calls require Pro. Existing Free accounts retain their assigned monthly allowances. Read the account's offerVersion, trial and limits rather than assuming every Free account has the same offer. Only describe a monthly reset when the account response supplies a non-null resetsAt. - Pro plan: $19.99/month base. - Local/mobile numbers: $2.00/month each - Toll-free numbers: $2.50/month each - SMS outbound: $0.015/message - Voice calls (inbound + outbound): $0.035/minute - AI voice calls (in or out): $0.40/minute - Call recording: $0.01/minute on top of voice - No setup fees. Cancel anytime. INSTRUCTIONS: - Use only the WHAT WE DO and PRICING sections above. Never make up features or numbers. - If asked about something not listed, say "Let me have someone follow up on that. Can I get your name and the best email or number to reach you?" - Keep replies under 2 sentences. - If the caller says goodbye, thank them and end the call promptly. NEVER: - Invent pricing tiers, discounts, or promotions that aren't listed above. - Promise specific delivery dates or outcomes. - Claim features we don't have (e.g. video calling, fax, MMS). - Stay on the line after the caller has said goodbye. ``` ### Public template endpoint: no auth required ``` GET https://api.agentcall.co/v1/calls/prompt-templates ``` Returns 5 production-ready templates. Each one has `[BRACKETED]` placeholders for your business details, plus a `firstMessage`, `systemPrompt`, `recommendedVoice`, and `maxDurationSecs`. Pick the closest match to your use case, replace the placeholders, and pass the result as `systemPrompt` to `POST /v1/numbers/:id/inbound-config` or `POST /v1/calls/ai`. | Template ID | Title | Best For | Recommended Voice | | --- | --- | --- | --- | | `receptionist` | Receptionist (Front Desk) | General-purpose front-desk answering. Start here if you're not sure which template fits. | shimmer | | `lead-qualifier` | Lead Qualifier (Sales) | Inbound B2B sales calls. Gathers BANT (Budget, Authority, Need, Timeline) and routes serious prospects to a human. | coral | | `appointment-booker` | Appointment Booker | Captures name, contact, and preferred slot, then promises a confirmation email. The AI doesn't touch a calendar. A human or backend finalizes. | sage | | `customer-support` | Customer Support (FAQ Deflection) | Answers a defined FAQ. Escalates anything outside the FAQ to a human. Reduces support load without breaking trust. | ash | | `call-screener` | Call Screener (Anti-Spam) | Filters unsolicited calls before they reach you. Useful for solo founders and anyone whose number gets scraped. | verse | ### Common mistakes to avoid - **Vague prompts** ("Be helpful"). Invitation to hallucinate. - **Conflicting instructions** ("be brief" + "explain in detail"). Unpredictable output. - **No fallback for "I don't know"**: leads to hallucination. Always include an explicit instruction. - **Missing call-end behavior**: AI keeps talking past goodbye. - **Trying to fit too much** in one prompt (10,000 char hard cap; one focused use case per number works best). ### When the AI needs truly live data System prompts are for stable facts. For per-call dynamic data (inventory, account balances, current availability), tool calling is the right answer, and it runs live on voice calls via the Action Bridge. Declare `tools` + `actionWebhook` on the number's inbound config and the voice AI calls them mid-call (with `context.channel: "voice"` and `context.callId` set), then speaks the real result. See "In-call voice tool calling" above. If you have not wired a tool for a given fact, have the AI gather the question and promise a callback rather than putting a dynamic number in the prompt. --- ## All 62 MCP Tools ### Numbers & provisioning (7 tools) - **provision_number**: Provision a new phone number. Parameters: `country` (US/CA), `type` (local/toll_free/mobile), optional `areaCode` (3-digit NANP, honored for local/mobile in both US and CA. E.g. '314' for St. Louis, '416' for Toronto). Returns number in E.164 format. - **list_numbers**: List all provisioned numbers. Optional filters: `status`, `country`, `type`. Paginated. - **get_number**: Get details of a specific number by ID. Returns status, capabilities, creation date. - **release_number**: Release a number, deactivating it and stopping billing. Irreversible. - **update_number_label**: Rename a phone number's label (the human-readable name shown in the dashboard). Pass null to clear. - **update_number_voice**: Change the inbound AI voice on an existing receptionist without touching the systemPrompt, firstMessage, recording flag, contextWebhook, or notify block. Use this when the user asks to switch voices on a configured number; calling configure_inbound_ai for a voice change would wipe every other field. - **update_number_language**: Change the inbound AI language on an existing receptionist without touching anything else. Parameters: `numberId`, `language` ('auto' / 'en' / 'es' / 'fr' / 'de' / 'it' / 'pt' / 'nl' / 'ja' / 'ko' / 'zh' / 'hi' / 'ar' / 'ru' / 'id' / 'tr' / 'pl' / 'uk' / 'vi' / 'ta' / 'ms' / 'ro' / 'el' / 'cs' / 'sv' / 'hu' / 'da' / 'fi' / 'no' / 'sk' / 'hr' / 'bg'). Use when the user says "switch the receptionist to Spanish" or "answer in French". 'auto' matches the caller's language naturally; specific codes pin the AI to that language even if the caller speaks another. ### Inbound AI voice (2 tools) New accounts receive a one-time 72-hour live trial with one US/Canada local number, 300 managed inbound AI seconds (five minutes) total, and five SMS send attempts to verified destinations. Card verification is required before number allocation; the clock starts when allocation succeeds. There is no monthly reset or automatic paid subscription. Live OTP extraction and outbound calls require Pro. Existing Free accounts retain their assigned monthly allowances. Read the account's offerVersion, trial and limits rather than assuming every Free account has the same offer. Only describe a monthly reset when the account response supplies a non-null resetsAt. - **disable_inbound_ai**: Disable inbound AI on a number. Clears the inbound config so future calls ring through to the default webhook handler. ### Premium voice (3 tools) - **list_premium_voices**: List the Premium Voice library (Pro add-on, $0.59/min). Returns each voice's id, name, description, gender, accent, and sample MP3 URL. No auth or plan gate on listing. Use the returned id with set_premium_voice. - **set_premium_voice**: Enable Premium Voice on a number, or change which premium voice it uses. Parameters: `numberId`, `voiceId` (from list_premium_voices). Premium Voice answers inbound calls with a higher-quality, brandable voice and bills at $0.59/min on top of Pro. Idempotent; never wipes the rest of the inbound config. Requires Pro + a card on file + inbound AI already configured (otherwise returns 403/402/409). Confirm the $0.59/min rate with the user before enabling. - **disable_premium_voice**: Disable Premium Voice on a number, reverting it to the standard inbound AI voice. Parameter: `numberId`. Preserves the rest of the inbound config. Idempotent. Pro plan only. ### BYOK voice billing (2 tools) - **set_byok_openai_key**: Switch a number's inbound + outbound AI voice billing from Managed ($0.40/min) to BYOK ($0.10/min) by attaching the customer's own OpenAI key. Parameters: `numberId`, `apiKey`. The key is validated against the voice API before saving (atomic), encrypted at rest, and never returned (only a redacted preview). Pro plan only. Confirm the user accepts paying OpenAI directly and has enough credit before invoking. - **disable_byok**: Clear the saved BYOK key on a number and revert billing to Managed mode ($0.40/min). Parameter: `numberId`. Preserves every other inbound config field (systemPrompt, voice, firstMessage, notify, contextWebhook, record). ### Outbound AI defaults (3 tools) - **set_outbound_defaults**: Save a reusable outbound AI agent on a number. Parameters: `numberId`, `systemPrompt`, optional `voice`, `language`, `firstMessage`, `maxDurationSecs`, `record`, `templateId`. The dashboard's Place AI call dialog hydrates from this on every click, so dialing 50 different recipients with the same persona is 50 single clicks instead of 50 retypes. Pro plan only. Independent of `configure_inbound_ai` on the same number. - **get_outbound_defaults**: Read the saved outbound agent on a number. Returns the same shape that was saved, or `null` if none is set. Also surfaced as the `outbound` field on `list_numbers` and `get_number` responses. - **clear_outbound_defaults**: Remove the saved outbound agent on a number. Future Place AI call dialogs open blank for that number again. ### SMS (7 tools) - **send_sms**: Send an SMS. Parameters: `from` (your number in E.164), `to` (recipient E.164), `body` (message text). - **get_inbox**: Get inbound SMS for a number. Optional `otpOnly: true` to filter for OTP messages only. Paginated. - **get_message**: Get a specific SMS by message ID. Returns full message body, OTP if detected, timestamps. - **wait_for_otp**: Wait for an OTP to arrive on a number. Parameters: `numberId`, `timeout` (ms, default 30000). Polls inbox and returns the OTP code as soon as it arrives. Ideal for automated QA testing of your own app's SMS verification. - **list_sms_conversations**: List the SMS conversation threads on this account (newest activity first, paginated). Each thread is one contact texting a number that has `smsMode: "ai"` or `"relay"`. Parameters: optional `limit`, `cursor`. - **get_sms_conversation**: Get one SMS thread by ID, including its recent messages (up to the last 50, oldest first) and a `context` object that classifies those messages by freshness (`recentMessages` for the last 7 days, `recentSubstantiveMessages` with greetings removed, `olderMessages`, plus a `freshness` block). Use `context` for any question about recency: the raw `messages` list can span months on a quiet thread. Parameter: `conversationId`. - **reply_to_sms_conversation**: Reply into an SMS thread. The reply path for relay mode (`smsMode: "relay"`): your own agent calls this when it has an answer, and AgentCall sends and threads the SMS. Parameters: `conversationId`, `body`, optional `idempotencyKey` (a duplicate key within 24h is a no-op so a retried agent never double-texts). Agent-scoped and opt-out-checked: refuses to text a recipient who sent STOP. Billed as one standard outbound text. ### Calls & speech (7 tools) - **initiate_call**: Start an outbound call. Parameters: `from` (your number), `to` (recipient), `webhookUrl` (for call events). - **list_calls**: List call history. Returns status, duration, direction, recording URLs. - **get_call**: Get details of a specific call by ID. Includes status, duration, recording URL. - **hangup_call**: Terminate an active call by call ID. - **initiate_ai_call**: Start an AI-powered voice call where AI handles the conversation autonomously. Parameters: `from` (your provisioned number), `to` (recipient), `systemPrompt` (instructions for AI), `voice` (alloy/ash/ballad/cedar/coral/echo/marin/sage/shimmer/verse, 10 options including the natural-sounding marin and cedar), `language` ('auto' default, or any of 31 ISO codes en/es/fr/de/it/pt/nl/ja/ko/zh/hi/ar/ru/id/tr/pl/uk/vi/ta/ms/ro/el/cs/sv/hu/da/fi/no/sk/hr/bg to make the AI speak that language for the entire call, e.g. booking a Spanish-speaking doctor's appointment on behalf of someone), `firstMessage` (verbatim, translate yourself if non-English), `maxDurationSecs`. Pro plan only, $0.40/min. Available on US and Canada numbers. - **get_call_transcript**: Get the transcript of a completed AI voice call. Returns conversation entries with timestamps, summary, and duration. - **synthesize_speech**: Generate standalone speech audio from text, off-call, using the same 10 voices as your calls. Parameters: `text` (1-4096 chars), `voice` (alloy/ash/ballad/cedar/coral/echo/marin/sage/shimmer/verse, default shimmer), `format` ('mp3' default, or 'wav'), optional `language` (ISO-639-1 hint). Returns the audio inline as a base64 content block plus a text summary. Use it for IVR prompts, voicemail greetings, generated show segments, and social clips so the voice identity carries off-call. Pro plan only, $0.03 per 1,000 characters. Per-request cap 4096 chars; per-agent daily cap 500,000 chars. ### Scheduling (3 tools) - **create_schedule**: Make a number's agent text someone FIRST, once or on a recurring cadence (e.g. "text an appointment reminder tomorrow at 9am", "send a payment follow-up every Monday"). Parameters: `numberId`, `contactPhone`, `kind` (reminder/digest/custom), either `fireAt` (one-shot ISO 8601) or `recurrence`, either `template` (sent verbatim with `{{placeholders}}` filled from `payload`) or `promptHint` (the agent composes the text itself), optional `timezone`, `payload`, `dedupeKey`. Pro plan only; billed as one outbound SMS when it fires. The recipient's reply flows into the same thread, so a follow-up like "C" is understood in context. Returns the schedule ID. - **list_schedules**: List the proactive schedules on a number, soonest `nextRunAt` first. Parameters: `numberId`, optional `status` filter. - **cancel_schedule**: Cancel and remove a proactive schedule by ID so it never runs again. Frees its `dedupeKey` for re-use. Parameters: `numberId`, `scheduleId`. ### Webhooks (4 tools) - **create_webhook**: Register a webhook endpoint. Parameters: `url` (HTTPS required), `events` (array of event types). Returns webhook secret for signature verification. - **list_webhooks**: List all registered webhooks with their subscribed events and status. - **rotate_webhook_secret**: Generate a new signing secret for a webhook. Old secret invalidated immediately. - **delete_webhook**: Deactivate and remove a webhook endpoint. ### Usage (1 tool) - **get_usage**: Get cost breakdown for a billing period. Parameters: `period` (YYYY-MM). Returns counts and costs for numbers, SMS, calls. ### Plan & upgrade (2 tools) - **get_plan**: Show which plan the account is on (Free or Pro), the Free limits, the remaining AI allowance and actual trial expiry or legacy reset, and a one-sentence `summary` the agent can read to the user (e.g. "You are on the Free plan with 2 of 5 remaining AI allowance and trial expiry or legacy reset. Pro is $19.99/mo plus usage."). No parameters. Read-only. Call it before explaining any `plan_limit_*` error. - **upgrade_to_pro**: Start an AgentCall Pro upgrade for the account that owns the API key. Returns a secure checkout link the human clicks to subscribe (as plain text and as `{ url }`). Pro is $19.99/mo plus usage (AI voice calls $0.40/min, SMS $0.015). Agents should call this the moment any tool fails with a `plan_limit_*` code, then show the human the link exactly as returned. Takes about a minute; the failed action can be retried right after. If the account is already on Pro it says so instead of returning a link. No parameters. ### AgentFM (1 tool) - **request_agentfm_callback**: Submit a callback request to AgentFM. The AI host will call the provided number back to record a short podcast conversation. Parameters: `phone` (E.164 format, e.g. +15551234567), optional `persona` (string, max 200 chars: what the guest wants to discuss). No AgentCall account or Pro plan required to use this tool. Deduplicated per phone per 24h so multiple calls to this tool for the same number are safe. Returns `{ status: "queued" | "already_pending" }`. ### Auditable Call Memory: Read (10 tools) - **list_contacts**: List all contacts (callers AgentCall has seen). Paginated. Returns name, phone, last call timestamp, memory entry count. - **get_contact**: Get a single contact by ID. Includes display name, phone, tags, and memory summary. - **get_contact_by_phone**: Look up a contact by E.164 phone number. Useful for matching an incoming caller before the call connects. - **get_next_call_context**: Fetch the pre-call context block AgentCall would inject for a specific caller on a specific number. Use this to preview what the AI will know before the next call. - **get_current_memory**: Get the active memory state for a contact: facts the AI considers current. - **get_memory**: Get a specific memory entry by ID. Includes the source transcript quote that produced it. - **get_memory_timeline**: Get the full chronological history of memory entries for a contact, including superseded versions. - **list_memory_candidates**: List proposed memory updates that are awaiting acceptance. Filtered by contact or by status. - **list_call_reports**: List structured Call Reports across calls. Filterable by number, contact, date range. - **get_call_report**: Get a single Call Report by call ID. Includes intent, action items, sentiment, and source-backed memory entries. ### Auditable Call Memory: Write & Briefs (10 tools) - **update_contact**: Edit a contact's display name, tags, or notes. - **purge_contact**: Delete a contact and all associated memory entries. Irreversible. Use for "right to be forgotten" requests. - **update_memory**: Edit a memory entry's content. The source transcript quote is preserved as audit trail. - **delete_memory**: Delete a memory entry. The transcript itself is untouched. - **approve_memory_candidate**: Accept a proposed memory update. Promotes the candidate to active memory. - **reject_memory_candidate**: Reject a proposed memory update. The candidate is discarded; no change to active memory. - **set_memory_enabled**: Turn the Cross-call memory toggle on or off for the account. When off, no extraction runs and no new memory entries are stored. - **list_briefs**: List briefs in the inbox: items flagged for human review (commitments, sensitive facts, conflicts). - **acknowledge_brief**: Mark a brief as seen without resolving it. - **resolve_brief**: Close a brief after human review. --- ## REST API Reference Base URL: `https://api.agentcall.co/v1` Auth: `Authorization: Bearer YOUR_API_KEY` ### Numbers - `POST /v1/numbers/provision`. Provision a new number. Body: `{ country, type }` - `GET /v1/numbers`. List numbers. Query: `?status=active&country=US`. Each number includes a `messaging` object with the number's A2P (10DLC) registration state: `{ state, detail, checkedAt }` where `state` is `registered` (texts deliver normally), `pending` (still provisioning with the carriers, typically about two hours, occasionally a few days; texts sent before it clears may be dropped), `action_needed` (registration failed at the carrier; retried automatically every 15 minutes), `not_applicable` (non-US-local numbers, which use a different compliance path), or `unknown` (not checked yet). Check this first when texts are not arriving from a new number. - `GET /v1/numbers/:numberId`. Get number details. Includes the same `messaging` object as the list endpoint. - `DELETE /v1/numbers/:numberId`. Release number New accounts receive a one-time 72-hour live trial with one US/Canada local number, 300 managed inbound AI seconds (five minutes) total, and five SMS send attempts to verified destinations. Card verification is required before number allocation; the clock starts when allocation succeeds. There is no monthly reset or automatic paid subscription. Live OTP extraction and outbound calls require Pro. Existing Free accounts retain their assigned monthly allowances. Read the account's offerVersion, trial and limits rather than assuming every Free account has the same offer. Only describe a monthly reset when the account response supplies a non-null resetsAt. - `GET /v1/numbers/:numberId/inbound-config`. Read the current inbound config. Returns `{ numberId, number, carrier, config }`. - `DELETE /v1/numbers/:numberId/inbound-config`. Clear the inbound config (returns 204). - `POST /v1/numbers/:numberId/outbound-defaults`. Save a reusable outbound AI agent on the number. Body: `{ "systemPrompt": "...", "voice": "sage", "language": "en", "firstMessage": "...", "maxDurationSecs": 600, "record": true, "templateId": "appointment-booker" }`. Pro plan only. The dashboard's Place AI call dialog hydrates from this on every click. - `GET /v1/numbers/:numberId/outbound-defaults`. Read the saved outbound agent. Returns the saved shape, or `null` if none is set. Also surfaced as the `outbound` field on `GET /v1/numbers` and `GET /v1/numbers/:numberId`. - `DELETE /v1/numbers/:numberId/outbound-defaults`. Clear the saved outbound agent (returns 204). ### SMS - `POST /v1/sms/send`. Send SMS. Body: `{ from, to, body }` - `GET /v1/sms/inbox/:numberId`. Get inbox. Query: `?otpOnly=true` - `GET /v1/sms/:messageId`. Get message, including its delivery outcome. `status` moves `queued` (handed to the carrier, no receipt yet) to `sent` (carrier accepted it, handset delivery unconfirmed) to `delivered` (confirmed on the recipient's handset) or `failed` (carrier rejected it). A `failed` message carries an `errorCode`; `40010` means the sending number is not registered for A2P texting yet, so check that number's `messaging` state. The 201 from `POST /v1/sms/send` means the carrier accepted the message, not that a handset displayed it, so poll this endpoint when delivery matters. - `GET /v1/sms-conversations`. List SMS threads (two-way AI and relay), paginated. Query: `?limit&cursor` - `GET /v1/sms-conversations/:id`. Get one thread + its recent messages (up to 50, oldest first) + `context`, the same messages classified by freshness (`recentMessages`, `recentSubstantiveMessages`, `olderMessages`, `freshness`). Use `context` for recency questions; the raw list can span months on a quiet thread. - `POST /v1/sms-conversations/:id/reply`. Reply into a thread (relay mode). Body: `{ body, idempotencyKey? }`. Opt-out-checked, idempotent for 24h. ### Proactive scheduling (make the agent text first) - `POST /v1/numbers/:numberId/schedules`. Schedule an outbound-initiated text. Body: `{ contactPhone, kind?, fireAt | recurrence, template | promptHint, timezone?, payload?, dedupeKey? }`. Provide exactly one of `fireAt` (one-shot ISO 8601, must be in the future) or `recurrence` (recurring), and exactly one of `template` (sent verbatim, `{{placeholders}}` filled from `payload`) or `promptHint` (the agent writes the message). `timezone` is an IANA name, default `America/Chicago`. A duplicate `dedupeKey` returns 409. Pro plan only. - `GET /v1/numbers/:numberId/schedules`. List schedules on a number, soonest `nextRunAt` first. Query: `?status=` - `DELETE /v1/numbers/:numberId/schedules/:scheduleId`. Cancel a schedule and free its `dedupeKey`. - Each fire is billed as one standard outbound SMS ($0.015) and honors the recipient's STOP opt-out. A per-number daily cap of 200 proactive sends bounds a runaway integration. The recipient's reply lands on the same SMS thread, so a number with `smsMode: "ai"` answers it in context automatically. ### Calls - `POST /v1/calls/initiate`. Start a plain (non-AI) call. Pro plan only. Rings the destination, speaks a short automated line on answer identifying the call as coming from an AI agent through AgentCall, and reports status to your webhook; there is no live audio behind it, so use `POST /v1/calls/ai` when the agent needs to talk. Each destination can be dialed up to 3 times per UTC day (numbers you own or verified are exempt); past that the API returns 429 `redial_limit_exceeded`. Body: `{ from, to, webhookUrl, metadata? }` - `POST /v1/calls/ai`. Start AI voice call. Body: `{ from, to, systemPrompt, voice?, firstMessage?, maxDurationSecs?, metadata?, liveTranscript? }`. Pro plan only, $0.40/min. `liveTranscript` (boolean, default false) opts this call into real-time `transcript.partial` webhooks: each finalized utterance fires within ~1-2s of speech (see "Live transcript streaming" and the Webhook Events list). The post-call `call.transcript` is unaffected and stays the authoritative record. `metadata` is an optional string-to-string map (up to 24 keys, 64-char keys, 512-char values, 2KB serialized; violations return 400 with a clear message) that AgentCall stores on the call, echoes on the 201 response and every call object, and includes verbatim in every webhook for that call. See "Tagging calls with metadata" below. - `GET /v1/calls`. List calls. Each call object includes `status` (canonical lifecycle below), `peerCallId` (the other leg of a same-account agent-to-agent call, null for normal calls), and `metadata` when it was set on dial. - `GET /v1/calls/:callId`. Get call details. Includes `status`, `peerCallId`, and `metadata` when set on dial. - `GET /v1/calls/:callId/report`. Get the structured Call Report for an AI call. The `report` field is the canonical shape, identical to the `call.report.ready` webhook's `data.report`: `{callId, reportId, generatedAt, intent, urgency, spam, briefWorthy, callerName, summary, nextCallContext, ownerBrief, entities[], facts[], preferences[], decisions[], commitments[], tasks[], risks[], unresolved[], nextAction, extractorVersion, redactedAt}`. Every evidence-bearing item carries a verbatim `quote` (e.g. `facts: [{text, scope, confidence, quote, subjectKey}]`). The old top-level `payload` field (raw extractor JSON with `evidenceQuote`) is deprecated, kept for one release; read `report` instead. Returns 202 while the report is still being generated, 404 if it never will be. - `GET /v1/calls/:callId/transcript`. Get AI call transcript (entries, summary, duration) - `POST /v1/calls/:callId/hangup`. End call #### Canonical call status lifecycle Every call object and the `call.status` webhook use ONE status vocabulary: `initiated` -> `ringing` -> `in-progress` -> `completed`. Calls that never connect end in one of three terminal failure states: `busy`, `no_answer`, or `failed`. #### Agent-to-agent calls: peerCallId When both sides of a call are numbers on the same account (your outbound AI agent calling your own inbound AI number), AgentCall creates two Call records, one per leg, and links them via `peerCallId`. The field appears on `GET /v1/calls`, `GET /v1/calls/:callId`, and in every call webhook payload (`call.inbound`, `call.status`, `call.transcript`, `call.recording`, `call.report.ready`). It is `null` for normal calls. ```json { "id": "call_out_7f2a", "direction": "outbound", "from": "+13145550100", "to": "+13145550199", "status": "completed", "duration": 64, "peerCallId": "call_in_9c4e" } ``` #### Tagging calls with metadata `POST /v1/calls/ai` and `POST /v1/calls/initiate` accept an optional `metadata` object: a string-to-string map under your control, up to 24 keys, keys up to 64 characters, values up to 512 characters, 2KB serialized total. Violations return 400 with a clear message. AgentCall stores the map on the call, echoes it on the 201 dial response and on `GET /v1/calls` / `GET /v1/calls/:callId`, copies it to the linked inbound leg of a same-account agent-to-agent call, and includes it verbatim in every subsequent webhook payload (`call.inbound`, `call.status`, `call.transcript`, `call.recording`, `call.report.ready`). Calls dialed without metadata omit the field entirely. Why it exists: an orchestrator dialing many agent calls can tag each call with its own IDs instead of joining on `callId` and timestamps. Example: a 24/7 AI radio station tags every call with the show and segment it belongs to. ```json { "from": "num_abc123", "to": "+15551234567", "useSavedAgent": true, "metadata": { "showId": "show_morning_drive", "segmentId": "seg_0042" } } ``` Every webhook for that call echoes the map back: ```json { "event": "call.status", "timestamp": "2026-06-09T14:02:11.000Z", "data": { "callId": "call_7f2a", "status": "completed", "peerCallId": null, "metadata": { "showId": "show_morning_drive", "segmentId": "seg_0042" } } } ``` The `initiate_ai_call` MCP tool and the SDK's `client.calls.initiateAI({ ..., metadata })` accept the same field. #### Live transcript streaming Set `liveTranscript: true` to watch an AI voice call as it happens. With it on, each finalized utterance during the call fires a `transcript.partial` webhook in real time (within ~1-2s of speech), in addition to the final post-call `call.transcript` (which is unchanged and remains the authoritative complete record). Default is false; nothing is emitted unless a webhook is subscribed to the `transcript.partial` event. Opt in per outbound call on `POST /v1/calls/ai`, or per inbound number on `POST /v1/numbers/:id/inbound-config`. Use it for any UI showing a call in progress: a switchboard, a support dashboard, a sales coach. Without it, a "watch your agent on the phone" view has to fake liveness from the post-call transcript. ```json { "from": "num_abc123", "to": "+15551234567", "systemPrompt": "You are the front desk for Acme Plumbing...", "voice": "sage", "liveTranscript": true } ``` Each `transcript.partial` delivery (HMAC-SHA256 signed in `X-AgentCall-Signature`, same as every other webhook): ```json { "event": "transcript.partial", "timestamp": "2026-06-09T14:30:07.456Z", "data": { "callId": "call_7f2a", "sequence": 2, "role": "ai", "text": "Thanks, I have your address. What's the issue you're seeing?", "timestamp": "2026-06-09T14:30:07.456Z" } } ``` `sequence` is a monotonic per-call integer starting at 0, so a consumer can order utterances and de-dup on `(callId, sequence)` if a delivery is retried. `role` is `ai` or `human`, the same semantics as the final transcript. Utterances arrive ordered. - `GET /v1/calls/voices`. List available AI voices with samples (public, no auth) - `GET /v1/calls/prompt-templates`. List 5 ready-made system prompt templates (receptionist, lead-qualifier, appointment-booker, customer-support, call-screener). Each has `[BRACKETED]` placeholders to fill in. Public, no auth. See https://agentcall.co/docs/voice-prompts for the full guide. ### Text to speech - `POST /v1/tts`. Generate standalone speech audio from text, off-call, using the same 10 voices as your calls. Body: `{ text, voice?, format?, language? }`. `text` is required, 1-4096 characters. `voice` is one of the 10 call voices (alloy/ash/ballad/cedar/coral/echo/marin/sage/shimmer/verse), default shimmer. `format` is `"mp3"` (default) or `"wav"`. `language` is an optional ISO-639-1 hint. Returns the raw audio bytes (`Content-Type: audio/mpeg` or `audio/wav`) plus an `x-agentcall-tts-characters` response header with the billed character count. Pro plan only (Free returns 403 `plan_limit_tts`). Metered at $0.03 per 1,000 characters. Per-request cap is 4096 characters; the per-agent daily cap is 500,000 characters (429 `tts_daily_cap` once exceeded). The same 10 voices used on calls mean the voice identity carries off-call: IVR prompts, voicemail greetings, generated show segments, social clips. ```bash curl -X POST https://api.agentcall.co/v1/tts \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text":"Thanks for calling Acme Plumbing. Please leave a message after the tone.","voice":"shimmer","format":"mp3"}' \ --output speech.mp3 ``` The `synthesize_speech` MCP tool and the SDK's `client.tts.synthesize({ text, voice, format, language })` call the same endpoint. The MCP tool returns the audio inline as a base64 content block plus a text summary; the SDK returns `{ audio (Uint8Array), contentType, format, characters }`. ### AgentFM - `POST /v1/agentfm/callback-request`. Submit a callback request. Body: `{ phone, persona? }`. `phone` is E.164. `persona` is an optional string (max 200 chars) describing what the guest wants to discuss. No auth required. Rate-limited: 3 requests per hour per IP. Deduplicated per phone per 24h. Returns `{ status: "queued" | "already_pending" }`. ### Webhooks - `POST /v1/webhooks`. Register webhook. Body: `{ url, events }` - `GET /v1/webhooks`. List webhooks - `POST /v1/webhooks/:webhookId/rotate`. Rotate secret - `DELETE /v1/webhooks/:webhookId`. Delete webhook ### Usage - `GET /v1/usage`. Usage breakdown. Query: `?period=2026-02` ### Contacts - `GET /v1/contacts`. List contacts. Paginated. Query supports filters by tag and last-call date. - `GET /v1/contacts/:contactId`. Get a contact by ID. - `GET /v1/contacts/by-phone/:phone`. Get a contact by E.164 phone number. - `PATCH /v1/contacts/:contactId`. Update display name, tags, or notes. - `DELETE /v1/contacts/:contactId`. Purge a contact and all associated memory. Irreversible. ### Call Reports - `GET /v1/reports`. List Call Reports, newest first. Query: `?contactId=...&intent=...&urgency=...&briefWorthy=true&limit=...&cursor=...` - `GET /v1/calls/:callId/report`. Get a single Call Report: the canonical `report` object (summary, intent, urgency, facts and commitments with verbatim quotes, nextCallContext, ownerBrief) plus memory candidates. - `GET /v1/numbers/:numberId/next-call-context`. Preview the pre-call context block AgentCall would inject for the next caller. Query: `?phone=+1...`. ### Memory - `GET /v1/contacts/:contactId/memory`. Get the active memory state for a contact. - `GET /v1/memory/:memoryId`. Get a memory entry by ID, including the source transcript quote. - `GET /v1/contacts/:contactId/memory/timeline`. Get the full chronological history of memory entries (including superseded versions). - `PATCH /v1/memory/:memoryId`. Edit a memory entry's content. Source quote preserved. - `DELETE /v1/memory/:memoryId`. Delete a memory entry. - `GET /v1/memory-candidates`. List proposed memory updates awaiting acceptance. Query: `?contactId=...&status=pending`. - `POST /v1/memory-candidates/:candidateId/approve`. Accept a proposed memory update. - `POST /v1/memory-candidates/:candidateId/reject`. Reject a proposed memory update. ### Briefs - `GET /v1/briefs`. List briefs in the inbox. Filterable by status. - `POST /v1/briefs/:briefId/acknowledge`. Mark a brief as seen. - `POST /v1/briefs/:briefId/resolve`. Close a brief after human review. ### Outbound trust (who a new account may contact) - `GET /v1/business-verification`. Current status. Returns `{ submitted, status, grantsOutboundPilot, submittedAt, reviewedAt, reviewNote }`. `grantsOutboundPilot` is the direct answer to "may this account contact people it has not verified, right now?" - `POST /v1/business-verification`. Submit or resubmit. Body: `{ legalName, website | ein, contactName, useCase, sampleMessage }`. Exactly one of `website` or `ein` is required. Pro plan only (403 `pro_plan_required` otherwise). Rate limited to 5/hour. Returns 201 with `grantsOutboundPilot`. Submitting lifts the destination restriction immediately; a human reviews afterwards. - `GET /v1/verified-destinations`. List individually verified numbers plus the account's current `trustState`. Cap is 25 on Pro, 5 on Free. - `POST /v1/verified-destinations`. Body: `{ phone }`. Sends a 6-digit code from one of your own numbers to that destination. - `POST /v1/verified-destinations/:id/confirm`. Body: `{ code }`. Confirms the code and permanently allows that destination. - `DELETE /v1/verified-destinations/:id`. Remove a verified destination. - Trust states: `restricted` (owned + prior-inbound + individually verified only), `pilot` (business verified: strangers allowed, subject to a daily new-recipient cap), `standard` (no destination gate, automatic after 7 days on Pro). A blocked send returns `403 destination_not_verified` naming both routes out. ### Account - `GET /v1/account`. Current plan (`free` or `pro`), plan limits (`maxNumbers`, `maxSmsPerMonth`, `maxCallMinutesPerMonth`, null means unlimited), usage this period, and on Free the inbound AI trial state `freeInboundAiVoice: { capSeconds, usedSeconds, remainingSeconds, resetsAt }`. SDK: `client.account.get()`. MCP: `get_plan`. - `POST /v1/account/upgrade`. Start a Pro upgrade for the account that owns the API key. No body. Returns `{ url }`, a secure Stripe Checkout link for a human to open; subscribing takes about a minute. Pro is $19.99/mo plus usage. Returns `400 upgrade_unavailable` if the account is already on Pro and `503 upgrade_not_configured` if checkout cannot be started (email support@agentcall.co). SDK: `client.account.upgrade()`. MCP: `upgrade_to_pro`. - Plan-gate errors (`403 plan_limit_*`, plus the Free-plan variants of `sms_starter_exhausted` and `destination_not_verified`) all share one shape: the message names the feature, the price ("Pro is $19.99/mo plus usage"), and how to upgrade, and the error body carries two extra fields: `upgradeUrl` (`https://agentcall.co/billing`, an absolute link a human can open) and `upgradeToolName` (`upgrade_to_pro`, the MCP tool an agent runs to get a checkout link). Example: `{ "error": { "code": "plan_limit_voice_ai", "message": "Outbound AI voice calls are a Pro feature. Pro is $19.99/mo plus usage (AI voice calls $0.40/min). To upgrade, run the upgrade_to_pro tool and share the link it returns with the user, or visit https://agentcall.co/billing. Takes about a minute.", "statusCode": 403, "upgradeUrl": "https://agentcall.co/billing", "upgradeToolName": "upgrade_to_pro" } }`. - `GET /v1/account/memory-enabled`. Read the current Cross-call memory toggle state. - `PATCH /v1/account/memory-enabled`. Body: `{ "enabled": true | false }`. Turn the Cross-call memory toggle on or off. ### Webhook Events - `sms.inbound`. Incoming SMS received - `sms.otp`. OTP code detected in incoming SMS - `call.inbound`. Inbound call started (pre-answer) - `call.ringing`. Inbound call ringing - `call.status`. Call status changed. Uses the canonical status vocabulary: `initiated` -> `ringing` -> `in-progress` -> `completed`, plus terminal failure states `busy`, `no_answer`, `failed` for calls that never connect. Same values as REST call objects. - `call.recording`. Call recording available (Pro, opt-in) - `call.transcript`. After an inbound AI call ends, AgentCall POSTs `{callId, duration, peerCallId, transcript[], summary{summary, callerName, intent, urgency, callbackBy, spam}}` to subscribed endpoints. The call's `metadata` map is included verbatim when it was set on dial (omitted otherwise). HMAC-SHA256 signed in `X-AgentCall-Signature` header. Retried with exponential backoff on non-2xx. Use `callId` as the dedup key. Full payload schema and reference integrations at https://agentcall.co/docs/post-call-webhook. - `transcript.partial`. The in-call complement to `call.transcript`. When `liveTranscript: true` is set on `POST /v1/calls/ai` (per outbound call) or a number's inbound-config (per inbound number), AgentCall POSTs each finalized utterance in real time (within ~1-2s of speech): `{callId, sequence, role, text, timestamp}`. `sequence` is a monotonic per-call integer starting at 0 so consumers can order and de-dup; `role` is `ai` or `human` (same semantics as the final transcript); utterances arrive ordered. The post-call `call.transcript` remains the authoritative complete record and is unchanged. Default off; requires a webhook subscribed to `transcript.partial` (nothing is emitted otherwise). Same HMAC-SHA256 signing in `X-AgentCall-Signature`. Use for any UI showing a call in progress (switchboard, support dashboard, sales coach). See "Live transcript streaming" below. - `call.report.ready`. After Auditable Call Memory extraction finishes, AgentCall POSTs the structured Call Report to subscribed endpoints. `data.report` is the canonical shape, identical to what `GET /v1/calls/:id/report` returns: summary, intent, urgency, callerName, nextCallContext, ownerBrief, and evidence-backed item arrays (entities, facts, preferences, decisions, commitments, tasks, risks) where each item carries a verbatim transcript `quote`. `data` also includes `callId`, `reportId`, `peerCallId`, and the call's `metadata` map when it was set on dial (omitted otherwise), plus the report's old flat fields as deprecated aliases for one release. Same HMAC-SHA256 signing in `X-AgentCall-Signature`. Use `callId` as the dedup key. Full reference at https://agentcall.co/docs/post-call-webhook and https://agentcall.co/docs/memory. - `number.released`. Phone number released back to the carrier pool ### API Discovery - `GET /`. Returns JSON map of all endpoints with request bodies, examples, auth format, and quickstart guide. Used by OpenClaw agents for auto-discovery. - `GET /llms.txt`. Plain-text, LLM-optimized complete API reference with all endpoints, request/response examples, and common workflows. Ideal for AI agents. Interactive API docs: https://api.agentcall.co/docs --- ## Key Features - **US and Canada Phone Numbers**: Local, mobile, and toll-free numbers routed via licensed US carriers. $2/mo for local/mobile, $2.50/mo for toll-free. Designed for agent-to-agent SMS, voice/AI voice calls, and OTP flows on apps you control. Not intended for signing up to consumer platforms (Stripe, Google, WhatsApp, banks) that block VoIP. - **Automatic OTP Extraction**: Incoming SMS containing verification codes are parsed automatically. The OTP is delivered to your webhook as a separate `sms.otp` event. - **Automated QA Testing**: Agent provisions a test number → enters it in your staging app → AgentCall reads the OTP → agent verifies the flow works end-to-end. No manual phone needed. - **Hosted MCP Server**: Zero-install setup at `api.agentcall.co/mcp`. Streamable HTTP transport with `Authorization: Bearer` auth. Works with Claude Desktop, Claude Code, Cursor, Windsurf, Hermes Agent, OpenClaw. - **OpenClaw Integration**: Two env vars and your OpenClaw agent has all 62 phone tools via auto-discovery. Also on ClawHub. - **Voice Calls**: Outbound and inbound. Full duplex, low latency. Call recording opt-in ($0.01/min, Pro only). - **AI Voice Calls**: Let your agent make phone calls with AI-powered conversation. Provide a system prompt and the AI handles the entire call, then returns a transcript. Pro plan only, $0.40/min Managed or $0.10/min BYOK (see BYOK Voice Billing section). Pass `record: true` to also capture an mp3 of the call ($0.01/min on top). - **Saved Outbound AI Agents Per Number**: Each Pro number can store a reusable outbound AI agent (systemPrompt, voice, language, firstMessage, maxDurationSecs, record, templateId). The dashboard Place AI call dialog hydrates from the saved agent on every click, so dialing 50 different recipients with the same persona is 50 single clicks instead of 50 retypes. Surfaces as the `outbound` field on every list/get response, alongside the existing `inbound` field. Save, update, or clear via the dialog, SDK (`setOutboundDefaults` / `getOutboundDefaults` / `clearOutboundDefaults`), MCP (`set_outbound_defaults` / `get_outbound_defaults` / `clear_outbound_defaults`), or REST (`POST` / `GET` / `DELETE /v1/numbers/:id/outbound-defaults`). Independent of the inbound AI receptionist on the same number. Full section above. - **Two-way AI SMS**: Set `smsMode: "ai"` on a number and inbound texts get an AI reply that follows your prompt, remembers the contact across voice and SMS, and can take real actions through the Action Bridge (the AI calls tools you host so it quotes real prices instead of guessing). STOP is honored before the AI runs. Managed-only, Pro plan, billed as one outbound text per reply. Reactive by default; use `send_sms` or a proactive schedule (`create_schedule`) when you want the agent to text first. - **Relay mode (text your own agent)**: Set `smsMode: "relay"` and AgentCall runs no LLM. It pushes each inbound text to your own agent (Hermes, OpenClaw, or custom) over an HMAC-signed webhook, and your agent replies on its own schedule via `POST /v1/sms-conversations/:id/reply` or the `reply_to_sms_conversation` MCP tool. One command installs the open-source relay next to your agent (`git clone https://github.com/Kintupercy/agentcall-hermes-bridge.git cd agentcall-hermes-bridge && npm install && npx wrangler login ./bootstrap.sh --install-consumer --number-id num_xxx --allow +15551234567`), so you write only a script that takes the text on stdin and prints the reply on stdout. The same number is callable and textable, so your agent has one phone identity. `allowedSenders` keeps it answering only its owner. Billed as plain texts, no relay fee. STOP is honored. Your agent can also open a thread first with `send_sms` or a proactive schedule; the contact's reply relays back to your webhook as usual. New accounts receive a one-time 72-hour live trial with one US/Canada local number, 300 managed inbound AI seconds (five minutes) total, and five SMS send attempts to verified destinations. Card verification is required before number allocation; the clock starts when allocation succeeds. There is no monthly reset or automatic paid subscription. Live OTP extraction and outbound calls require Pro. Existing Free accounts retain their assigned monthly allowances. Read the account's offerVersion, trial and limits rather than assuming every Free account has the same offer. Only describe a monthly reset when the account response supplies a non-null resetsAt. - **BYOK Voice Billing (Bring Your Own OpenAI Key)**: Per-number choice between Managed ($0.40/min, AgentCall handles the OpenAI key) and BYOK ($0.10/min, customer brings their own OpenAI API key and pays OpenAI directly for model usage). Persistent caller Memory works identically in both modes. Full section below with the 3 endpoints, curl examples, competitor comparison, and what's not supported. - **Call Recording + Dashboard Playback**: Recordings stored in AgentCall's private storage for 1 year (then auto-purged). Listen from the Logs → Calls tab in the dashboard, or fetch a fresh signed URL via `GET /v1/calls/:id/recording` for embedding/integration. The `call.recording` webhook also delivers a 24-hour signed URL when each recording is ready. Standard outbound calls (`POST /v1/calls/initiate`) record when `record: true` is passed; AI voice paths add the TCPA auto-prepend; the dev caller controls disclosure on the standard path. - **Webhook Delivery**: HMAC-SHA256 signed payloads. Retry with exponential backoff. Events: sms.inbound, sms.otp, sms.ai_reply, call.inbound, call.ringing, call.status, call.recording, call.transcript, call.report.ready, transcript.partial, number.released. - **Per-Agent Isolation**: Each agent gets dedicated numbers. Ban or compromise of one never affects others. - **Multi-Country**: US and CA numbers available now. --- ## Pricing ### New Live Trial See the generated current-offer section above. One eligible local number, five inbound AI minutes total and five SMS attempts to verified destinations over 72 hours. Card verification first. No automatic subscription, monthly reset or Free overage. Existing Free accounts keep their assigned monthly offer. ### Pro Plan ($19.99/month + usage) - Unlimited numbers - Local/mobile numbers: $2.00/month each - Toll-free numbers: $2.50/month each - SMS outbound: $0.015/message - SMS inbound: $0.008/message New accounts receive a one-time 72-hour live trial with one US/Canada local number, 300 managed inbound AI seconds (five minutes) total, and five SMS send attempts to verified destinations. Card verification is required before number allocation; the clock starts when allocation succeeds. There is no monthly reset or automatic paid subscription. Live OTP extraction and outbound calls require Pro. Existing Free accounts retain their assigned monthly allowances. Read the account's offerVersion, trial and limits rather than assuming every Free account has the same offer. Only describe a monthly reset when the account response supplies a non-null resetsAt. - Two-way AI SMS (AgentCall's model answers texts for you): Pro only, $0.015 per reply, no AI surcharge - Voice calls (inbound + outbound): $0.035/minute - AI voice calls (outbound): $0.40/minute Managed (AgentCall handles the OpenAI key) or $0.10/minute BYOK (you supply the OpenAI API key and pay OpenAI directly for model usage) - Inbound AI voice: $0.40/minute Managed or $0.10/minute BYOK (US and Canada numbers only). Per-number selection, swap any time. New accounts get 300 managed inbound AI seconds total within a one-time 72-hour live trial, with card verification before number allocation. Exhaustion or expiry stops new trial calls; it does not create paid overage or a monthly reset. Existing Free accounts keep their assigned monthly offer. Read get_plan or GET /v1/account before quoting limits. - Call recording: $0.01/minute - Cross-call memory included with no separate charge on top of the per-minute AI voice rate. Works identically under Managed and BYOK. - Priority OTP delivery - Email support ### Agent Startup Tier ($189/month, Coming Soon, waitlist open) Phone identities for your AI employees. A predictable monthly bundle that gives every agent its own phone identity with SMS, voice, call history, and webhooks. The tier is agent-agnostic. Assign identities to a Hermes Chief of Staff, a custom receptionist, an outbound sales agent, a call-screener, a survey runner, or anything else you can describe in a system prompt. Build-your-own positioning: customers configure each agent themselves using AgentCall's APIs and setup guides (Hermes worked example at `/docs/hermes`, voice prompt templates at `/docs/voice-prompts`). Bundle includes: - Everything in Pro, plus: - 3 phone identities included (mix local + toll-free, one number per agent) - 500 SMS / month across all agents (inbound + outbound combined) - 500 voice minutes / month (8+ hours of talk time) - 250 AI voice minutes / month (~50 short AI calls or ~25 longer ones) - Per-agent call history and webhooks (every event scoped to the owning agent) - Pay-as-you-go overage at standard Pro rates - Slack channel support (dedicated channel for Agent Startup customers) Join the waitlist at https://agentcall.co/waitlist with an email and we'll notify you the day the tier opens. White-glove "we build your agent for you" setup is available as a separate consulting engagement, not bundled into the $189 price; reach out via https://agentcall.co/contact to scope. No setup fees. No minimums. Cancel anytime. --- ## Frequently Asked Questions ### How do I give my AI agent a phone number? Sign up at agentcall.co, get your API key, and call `POST /v1/numbers/provision` with `{ "country": "US", "type": "local" }`. Your agent gets a real phone number in seconds. Or use the MCP server. Your agent can provision numbers directly with the `provision_number` tool. ### What's the best phone API for AI agents? AgentCall is purpose-built for AI agents. Unlike Vonage and other human-developer telecom APIs, AgentCall provides automatic OTP extraction, per-agent number isolation, a 62-tool MCP server with 5 prompts that drops into Claude/Cursor/Windsurf, inbound AI voice (incoming calls answered autonomously), one-call AI voice outbound with saved per-number agent personas, post-call email summaries, and Auditable Call Memory so returning callers get personalized context. The free tier lets you prototype without a credit card. ### How do I set up AgentCall with OpenClaw? Set two environment variables: `AGENTCALL_API_KEY=your_key` and `AGENTCALL_API_URL=https://api.agentcall.co`. Restart OpenClaw. Your agent hits the discovery endpoint and auto-learns all 62 tools. Alternatively, install the AgentCall skill from ClawHub. ### How do I connect AgentCall to Claude Desktop, Claude Code, Cursor, Windsurf, or Hermes Agent? Use the hosted MCP server. Add this to your MCP config: `{ "transport": "streamable-http", "url": "https://api.agentcall.co/mcp", "headers": { "Authorization": "Bearer YOUR_KEY" } }`. No Node.js or npx needed. For OpenAI Codex specifically, use the stdio config in `~/.codex/config.toml` (TOML, not JSON; Codex doesn't yet support remote HTTP MCP). ### What's the difference between AgentCall and Twilio for AI agents? Same underlying telephony category (VoIP through licensed carriers), different product surfaces. Twilio is a programmable-telecom toolkit for human developers building call centers and 2FA flows. AgentCall is shaped for AI agents: a 62-tool MCP server with 5 prompts that drops into Claude/Cursor/Windsurf, automatic OTP extraction from incoming SMS, one-call AI voice (inbound and outbound) with saved per-number agent personas, per-agent number isolation, post-call email summaries, and Auditable Call Memory that lets the AI recognize returning callers. If your LLM is the user of the API, AgentCall is the right shape. If a human is, Twilio still wins. ### How do I receive OTP codes with an AI agent? Provision a number, enter it in your app's verification form, and use the `wait_for_otp` tool (or poll `GET /v1/sms/inbox/:numberId?otpOnly=true`). AgentCall automatically detects and extracts OTP codes from incoming SMS and delivers them to your webhook as an `sms.otp` event. This is designed for testing your own applications' SMS flows. ### Is there an MCP server for phone numbers? Yes. AgentCall provides a hosted MCP server at `api.agentcall.co/mcp` (zero install, streamable HTTP), a Smithery listing at https://smithery.ai/server/agentcall/agentcall, and a local server via `npx @agentcall/mcp-server`. All three expose the same 62 phone tools and 5 prompts to AI agents in Claude Desktop, Claude Code, Cursor, Windsurf, Hermes Agent, OpenClaw, and other MCP clients. ### What's the difference between the hosted and local MCP server? The hosted server runs at `api.agentcall.co/mcp`. No Node.js, no npx, no local process. Just paste the config and restart. The local server runs on your machine via `npx @agentcall/mcp-server`, requires Node.js 20+, works offline after first download. Both provide the same 62 tools. ### Do AgentCall numbers pass phone verification? AgentCall numbers are VoIP, routed through licensed US carriers. They work for OTP flows on apps you control (your own SaaS verification, dev/staging QA, agent-to-agent SMS) and for services that accept VoIP. They will likely be rejected by consumer platforms that explicitly block VoIP for signup (Stripe, Google, Apple, WhatsApp, banks). If you need numbers that pass strict consumer-platform verification, AgentCall isn't the right fit today; an eSIM-backed integration is on our roadmap. ### What countries does AgentCall support? Currently: United States and Canada. More countries are being added based on demand. You can provision local, toll-free, and mobile numbers in all supported countries. ### Does the AI remember callers between calls? Yes. Auditable Call Memory is cross-call memory for AI phone agents that remembers every caller, with audit-trail evidence quotes. Every AI call produces a structured Call Report and source-backed memory entries. Returning callers automatically get personalized context: the AI knows their name, recent topics, and outstanding commitments. Every memory entry quotes the transcript line that produced it, so you can audit, edit, or delete what the AI "remembers." Controlled by a Cross-call memory toggle on the dashboard, ON by default for Free and Pro. Full reference at https://agentcall.co/docs/memory. ### How do I turn cross-call memory off? Flip the Cross-call memory toggle on the dashboard at https://agentcall.co/dashboard, or call `set_memory_enabled` via MCP, or `PATCH /v1/account/memory-enabled` with `{ "enabled": false }`. When off, no extraction runs and no new memory entries are stored. ### Why am I getting 403 destination_not_verified when my agent sends a text or places a call? Your account is new and still in the `restricted` trust state, and the number you are contacting is not one it is allowed to reach yet. Restricted accounts can contact numbers they own, anyone who contacted them first (30-day window), and numbers verified individually by code; the SMS starter allowance also lets Free text up to 10 different new numbers lifetime, and a new Pro account up to 25 during its first week. Everything else is blocked. Two ways out: `POST /v1/business-verification` with your legal name, website or EIN, use case, and a sample message, which lifts the restriction in minutes on Pro; or `POST /v1/verified-destinations` to verify that one number by having a 6-digit code sent there and read back. On Pro the restriction also lifts on its own after 7 days. The error message itself names both routes. ### Why am I getting 403 sms_starter_exhausted when my agent sends a text? Your account has texted its lifetime allowance of new numbers under the SMS starter allowance (10 on Free, 25 on a new Pro account). You can still text numbers you have verified by code (`POST /v1/verified-destinations`), your own AgentCall numbers, and anyone who texted or called you first, and you can keep texting the numbers already covered. For texting beyond that: on Pro, submit a business verification (lifts the restriction in minutes) or wait out the 7-day window; on Free, upgrade to Pro. ### Can my AI agent text my own customer list on day one? Yes, on Pro, after submitting a business verification. Sign up, provision a number, and `POST /v1/business-verification` with your legal business name, a website or EIN, what you will be texting people about, and one sample message. The restriction lifts immediately and your agent can contact people it has never texted, using `send_sms` for an immediate message or `create_schedule` for reminders and follow-ups. A daily cap on new recipients applies while the account is under 7 days old (25 on day one, rising through the week), which suits a real pilot; it lifts automatically after that. The whole thing takes about three minutes. ### Do I have to register a 10DLC campaign with AgentCall before sending SMS? No. AgentCall registers your number on its own 10DLC campaign for you, which is the step that normally costs days to weeks of TCR brand and campaign approval on other platforms before your first A2P message sends at all. What AgentCall asks instead is a business verification during your first week, and that clears in minutes rather than days. Net, you send sooner than the alternative. Registration on our campaign usually completes in about two hours and occasionally takes longer, and you can watch it: `GET /v1/numbers` returns a `messaging.state` per number (`registered`, `pending`, `action_needed`), and the dashboard Numbers page badges anything that is not ready. Provision a number a little ahead of when you need it rather than minutes before a launch. ### How do I know if my text was actually delivered? Read the message back with `GET /v1/sms/:messageId` (or the `get_message` tool over MCP). Its `status` moves `queued` to `sent` to `delivered` or `failed`, and a failed message carries an `errorCode`. The 201 you get from `POST /v1/sms/send` only means the carrier accepted the message for delivery, so it is not proof a handset displayed it. An `errorCode` of `40010` means the sending number is not registered for A2P texting yet, so check that number's `messaging.state`. This matters more for agents than for humans: an agent sends unattended, so nothing notices silence unless it checks. ### What does AgentCall do to stop its numbers being used for spam or scams? Several layers, and they are why a new account cannot text a cold list immediately. New accounts can only reach numbers they own, prior inbound contacts, or individually verified destinations (plus a content-screened SMS starter allowance: 10 new numbers lifetime on Free, 25 on a new Pro account), until a business verification or 7 days on Pro lifts that. Outbound volume is capped per account per day and ramps with account age. AI call scripts are screened for scam patterns before any carrier spend. STOP, UNSUBSCRIBE, CANCEL, END, and QUIT are honored before any AI or relay runs, and an opted-out contact is skipped on every future send including scheduled ones. Every refusal is written to an auditable log, and a nightly sweep looks for ban-evasion patterns. --- ## Who It's For - **AI Agent Developers**: Phone numbers for autonomous agents. SMS, voice, OTP testing, and notifications. - **Builders of AI Agents Who Want a Real Receptionist Experience**: A phone presence that recognizes returning callers, remembers what was discussed, and gives the AI source-backed context on every call. - **Small Businesses Who Want Their AI to Answer Texts on Their Behalf**: Handle inbound customer texts 24/7 (quotes, bookings, FAQs) the same way the AI already answers calls, pulling real prices and availability from their own systems via the Action Bridge instead of guessing. - **Developers Who Want to Text Their Own Agent**: Point a real number at your Hermes, OpenClaw, or custom agent with `smsMode: "relay"` and text it like you would over any chat app ("check my emails", "what's critical this week"). AgentCall handles the phone number, threading, and opt-outs; your agent does the thinking. `allowedSenders` keeps it answering only you, and one number serves both voice and text. - **OpenClaw Users**: Two env vars and your agent has phone capabilities. Also available on ClawHub. - **Claude Desktop / Cursor / Windsurf Users**: Hosted MCP server. Zero install phone tools for AI coding assistants. - **Browser Automation Teams**: Real phone numbers for automated testing and notifications. - **AI Voice Agent Builders**: Programmable numbers for inbound and outbound AI voice conversations. - **SaaS Platforms**: Integrate phone capabilities into AI-powered products. --- ## Videos Walkthroughs and demos on the [stlpercy YouTube channel](https://www.youtube.com/@stlpercy): - **[Hermes Agent Makes Phone Calls: I Built an AI Voice Agent with AgentCall](https://www.youtube.com/watch?v=d5C7HCEsuho)** (13:04, 2026-05-23). Personal Hermes agent ("Laura") connected to a real phone number via AgentCall. Cross-call memory, post-call email summaries, HVAC after-hours receptionist use case. - **[Hermes + AgentCall: Build Voice AI Agents for Small Businesses](https://www.youtube.com/watch?v=_eWev4IUCPI)** (14:15, 2026-05-24). Step-by-step setup of AgentCall with Hermes for a 24/7 AI receptionist at a plumbing company. Provisioning, system prompts, live test call. - **[Hermes Agent Gets a Phone Number: One AI Agent, $10K/Month?](https://www.youtube.com/watch?v=J0vlNK80Ha4)** (14:25, 2026-05-26). End-to-end dashboard walkthrough for outbound + inbound AI voice calls, multilingual support (English/Spanish), call memory, and the $10K/month math for selling AI phone agents to local-services businesses. ## Distribution - **Smithery**: [agentcall/agentcall](https://smithery.ai/server/agentcall/agentcall). MCP server with 62 tools and 5 prompts, discoverable by any MCP-compatible agent. - **npm SDK**: [agentcall](https://www.npmjs.com/package/agentcall). Node.js SDK. - **npm MCP Server**: [@agentcall/mcp-server](https://www.npmjs.com/package/@agentcall/mcp-server). Run locally via `npx @agentcall/mcp-server`. - **AgentCall MCP Endpoint**: `https://api.agentcall.co/mcp` (streamable HTTP, `Authorization: Bearer ac_live_...`). - **Smithery-hosted MCP URL**: `https://agentcall--agentcall1.run.tools` (Smithery handles OAuth and token refresh). - **YouTube**: [stlpercy channel](https://www.youtube.com/@stlpercy). Setup walkthroughs and small-business use-case demos for AgentCall + Hermes. ## Documentation Links - [Documentation Index](https://agentcall.co/docs). Canonical entry point. Card-driven index with workflow-grouped sidebar (Get Started / Integrations / Webhooks / Reference) on every docs page for cross-navigation. - [Product Overview](https://agentcall.co/product) - [MCP Server Docs](https://agentcall.co/docs/mcp). Hosted and local setup. - [SMS for AI Agents](https://agentcall.co/docs/agent-sms). The agent-texting guide: send with POST /v1/sms/send (idempotency keys for retried jobs), read replies by inbox polling or relay-mode webhook push, reply in-thread, action bridge tools, day-one trust rules, delivery troubleshooting. - [Writing System Prompts for AI Voice Calls](https://agentcall.co/docs/voice-prompts). 4-section structure plus 5 templates. - [Hermes Integration (Pre-call Context)](https://agentcall.co/docs/hermes). Walkthrough with Telegram-prompt examples for wiring a Hermes agent to AgentCall via a Cloudflare Worker bridge. - [OpenClaw Integration](https://agentcall.co/docs/openclaw). Env vars plus ClawHub. - [Post-Call Webhook](https://agentcall.co/docs/post-call-webhook). Subscribe to `call.transcript` for the full transcript and LLM summary after every inbound AI call. HMAC-signed, retried automatically. - [Auditable Call Memory](https://agentcall.co/docs/memory). Contacts, Call Reports, source-backed memory, briefs inbox, contextSource modes, and the `call.report.ready` webhook. - [Cross-Call Memory Landing](https://agentcall.co/cross-call-memory). Product overview of Auditable Call Memory for AI phone agents. - [Contacts Dashboard](https://agentcall.co/contacts). Every caller, name, last call, full memory timeline. - [Inbox Dashboard](https://agentcall.co/inbox). Briefs surfaced for human review. - [AgentCall on Smithery](https://smithery.ai/server/agentcall/agentcall). MCP registry listing. - [Pricing](https://agentcall.co/#pricing) - [FAQ](https://agentcall.co/faq) - [Blog](https://agentcall.co/blog) - [API Reference](https://api.agentcall.co/docs). Interactive OpenAPI docs. - [Contact](https://agentcall.co/contact) - [Terms](https://agentcall.co/terms) - [Privacy](https://agentcall.co/privacy) ## Node.js SDK ``` npm install agentcall ``` ```javascript import AgentCall from 'agentcall' const ac = new AgentCall('ac_live_xxxxxxxx') // Provision a number. Optional areaCode targets a specific 3-digit NPA const number = await ac.numbers.provision({ country: 'US', type: 'local', areaCode: '314' }) // Send SMS await ac.sms.send({ from: number.number, to: '+12125559876', body: 'Hello' }) // Wait for OTP (polls inbox, returns code when it arrives) const otp = await ac.sms.waitForOTP(number.id, { timeout: 30000 }) console.log(otp) // "847291" ``` Published on npm: https://www.npmjs.com/package/agentcall