Voice API
Place outbound phone calls from your AI agent with a single API request. Give the bot a goal, the facts it needs, and a script to follow - it handles the rest. Debt collection, appointment reminders, sales follow-ups, customer surveys - any call where you know what the bot should accomplish.
How it works
One POST request places the call. Your bot dials the number, greets the recipient, carries the conversation based on the goal and facts you provided, and ends when the objective is met.
POST /v1/voice/calls
Authorization: Bearer mbn_live_...
Content-Type: application/json
Idempotency-Key: <unique-uuid>
{
"from": "+16473705600",
"to": "+16475551234",
"triggeredBy": "your-system-name",
"task": {
"goal": "What the bot should accomplish",
"script": "which-playbook-script-to-follow",
"facts": { "key": "value pairs the bot can reference" },
"escalation": "When and how to escalate"
}
}The bot handles the conversation autonomously - verifying the recipient, delivering the message, handling objections, and closing the call. Every call is recorded, audited, and rate-limited.
Real example: debt collection call
Let's say your company Vello needs to collect $154.56 from a customer named Bobby for 1,000 SMS credits. Here's the API call:
curl -X POST "https://api.talktomyagent.io/v1/voice/calls" \
-H "Authorization: Bearer mbn_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"from": "+16473705600",
"to": "+16478359044",
"targetName": "Bobby",
"triggeredBy": "vello-billing-system",
"maxDurationSecs": 300,
"task": {
"goal": "Collect $154.56 from Bobby for SMS credits; offer payment link; escalate if refused.",
"script": "debt-collection",
"facts": {
"debtor_name": "Bobby",
"amount_due": "$154.56",
"currency": "USD",
"due_date": "May 25, 2026",
"purchase": "1,000 SMS credits",
"payment_link": "https://pay.vello.io/inv/bobby-154",
"creditor": "Vello"
},
"escalation": "If customer refuses to pay, use openclaw_query to email joe@vello.io with a one-line summary."
}
}'What the bot does
Bobby's phone rings. When he picks up, the bot says something like:
From there, the bot handles Bobby's response naturally:
- ✓Bobby agrees to pay - bot confirms and sends the payment link via SMS
- ⏳Bobby needs more time - bot asks for a specific date and logs it
- ✗Bobby refuses - bot closes politely and escalates to joe@vello.io
- ?Bobby disputes the charge - bot says someone will follow up and ends the call
The bot knows what to do because you configured a script profile in your agent's playbook - a simple markdown file on disk. See Setting up script profiles below.
The "task" object
The task object is the structured brief you hand the bot for each call. It has four fields:
goal required
One sentence describing what success looks like for this call. The bot reads this as its primary objective.
"goal": "Collect $154.56 from Bobby for SMS credits; offer payment link; escalate if refused."facts optional
Key-value pairs the bot can reference during the call. The bot will ONLY use these facts - it won't invent data. Values can be strings, numbers, or booleans.
"facts": {
"debtor_name": "Bobby",
"amount_due": "$154.56",
"due_date": "May 25, 2026",
"payment_link": "https://pay.vello.io/inv/bobby-154"
}Limits: up to 30 keys, string values up to 500 characters, total payload under 4 KB.
script optional
The name of a script profile in your agent's voice playbook. This tells the bot HOW to handle this type of call - the opening line, response branches, hard limits, and closing behavior.
"script": "debt-collection"If the named script doesn't exist in your playbook, the bot falls back to general outbound guidance and a "script-not-found" warning appears in the response.
escalation optional
A plain-English rule telling the bot when and how to escalate. The bot acts on this via your agent's installed tools (email, CRM, etc.) - the platform doesn't enforce a specific escalation method.
"escalation": "If customer refuses to pay, use openclaw_query to email joe@vello.io with a summary."You can also use the legacy purpose field (a single free-text string) instead of task for simpler calls. The two are mutually exclusive - provide one or the other, not both.
Setting up script profiles
Script profiles live in your agent's voice playbook - a markdown file on disk at protocols/dojo-voice-agent-playbook.md. Each profile is a #### heading under the ## Outbound Mode > Script profiles section.
Here's what the debt-collection profile looks like:
#### debt-collection
**Goal recap**: collect the amount due, offer the payment link.
**Expected facts**: debtor_name, amount_due, due_date,
payment_link, creditor, purchase
**Opening** (verify identity first):
"Hi, is this {facts.debtor_name}? - Hi {facts.debtor_name},
this is {AGENT_NAME} on behalf of {facts.creditor} about your
{facts.purchase} purchase, {facts.amount_due}, that was due
{facts.due_date}."
**Branches**:
- Agrees to pay -> confirm + share {facts.payment_link} + close
- Refuses -> run openclaw_query to email the escalation contact
- Disputes -> "I'll have someone email you the details" + close
- Voicemail -> leave brief message + close
- Wrong person -> apologize + close
**Hard limits**: no discounts, no credit-reporting threats
**Duration**: under 3 minutesThat's it. Pure markdown. No code, no tools to install. Your bot reads this at call time, combines it with the facts you sent in the API request, and handles the conversation.
Want multiple call types? Stack them in the same playbook. Add #### appointment-confirmation, #### reminder, #### sales-discovery - each with its own opening, branches, and limits. The script field in the API request picks which one to use.
Getting started
1Get an API key
Go to app.talktomyagent.io → API Keys. Create a key with the voice:call scope and bind it to your voice deployment.
2Place your first call
Use your agent's phone number (from the Agent Phones page) as the from field:
curl -X POST "https://api.talktomyagent.io/v1/voice/calls" \
-H "Authorization: Bearer mbn_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"from": "+1YOUR_AGENT_PHONE",
"to": "+1YOUR_PHONE_NUMBER",
"purpose": "Call to confirm that the API integration is working. Say hello, chat briefly, then say goodbye.",
"triggeredBy": "first-test"
}'Your phone should ring within 2 seconds. Once confirmed, switch to the structured task format for production calls.
Response
Success (200)
{
"data": {
"callControlId": "v3:abcDef123...",
"expectedDialAt": "2026-05-23T19:34:15.000Z",
"warnings": []
}
}callControlId is the unique identifier for the placed call - use it to correlate with recordings and audit logs. warnings surfaces soft issues (e.g. "script-not-found" if your script name doesn't match a playbook profile).
Errors
All errors use RFC 7807 problem+json format:
| Status | Error | What it means |
|---|---|---|
| 400 | validation-failed | Invalid phone number, missing fields, or task failed sanitization |
| 400 | voice-self-call | Target number is the deployment's own number |
| 403 | voice-country-blocked | Target country not in your outbound allowlist |
| 404 | not-found | Deployment doesn't exist or your key isn't scoped to it |
| 409 | voice-not-provisioned | Voice deployment isn't fully set up yet |
| 429 | rate-limit-exceeded | Hit the per-target (5/hr), hourly (30/hr), or daily (100/day) limit |
| 502 | dial-failed | Telecom provider dial failure - retry later |
What you can build
Same API, same pattern. Write a script profile for each call type, send the matching task with facts - your bot handles the rest.
Debt collection
Collect payments, offer payment links, escalate refusals to a human.
View full tutorialAppointment reminders
Confirm, reschedule, or cancel upcoming appointments.
View full tutorialSales follow-ups
Initial discovery calls, lead qualification, schedule demos.
View full tutorialNotifications & alerts
Delivery updates, service outage notices, policy changes.
View full tutorialSecurity & compliance
- Authentication: Bearer API key with
voice:callscope, bound to specific deployments. - Idempotency: every request requires an
Idempotency-Keyheader to prevent duplicate calls on retries. - Rate limits: 5 calls/hour to the same number, 30 calls/hour, 100 calls/day per deployment.
- Country allowlist: defaults to US only (one-party consent). Add countries via your voice portal settings.
- Audit trail: every call writes a compliance audit row with the full task, caller ID, and outcome.
- Prompt sanitization: all text fields are screened for prompt-injection patterns before reaching the bot.
Important: The task.goal, task.facts, and task.escalation fields become part of the bot's instructions. Never pass through untrusted end-user input in these fields - always validate or author them server-side.
Full API reference
The complete OpenAPI specification with all parameters, schemas, and error codes is available as interactive documentation: