Quick Start
Make your first AI phone call in 2 minutes.
1. Get an API Key
Sign up at agenticcalling.ai/dashboard and create an API key. Free tier includes 5 minutes/month, 1 phone line, and 10 SMS. A US phone number is automatically provisioned.
2. Connect via MCP (Recommended)
The fastest way to start making calls — connect the MCP server to Claude or ChatGPT and just ask it to call someone.
Add to your claude_desktop_config.json or .claude/settings.json:
{
"mcpServers": {
"agenticcalling": {
"command": "uvx",
"args": ["agenticcalling-mcp"],
"env": { "AC_API_KEY": "ac_live_..." }
}
}
}
Go to Settings → Integrations → Add MCP Server and enter:
https://mcp.agenticcalling.ai/sse
Paste your API key when prompted.
Go to Settings → Connected Apps → Add and enter the MCP server URL:
https://mcp.agenticcalling.ai/sse
Paste your API key when prompted. All 14 calling tools are now available.
3. Ask Your AI to Make a Call
Once connected, just talk to Claude or ChatGPT naturally:
"Call +1 415 555 1234 and ask what time they close today"
"Call these 50 hotels in Miami and find me the cheapest rate for March 15-18"
"Call every supplier on my list and negotiate steel below $50/unit"
"Run a phone survey with these 200 customers about their satisfaction"
Your AI will use the make_call tool, wait for completion, and return the transcript, summary, and any structured data — all in the same conversation.
Alternative: Python SDK / REST API
For programmatic access, use the Python SDK or call the REST API directly.
pip install agenticcalling
from agenticcalling import AgenticCalling
client = AgenticCalling(api_key="ac_live_...")
call = client.calls.create(
to="+14155551234",
objective="Ask what time they close today"
)
print(call.call_id) # queued
result = client.calls.get(call.call_id)
print(result.summary)
print(result.structured_data)
# {"closing_time": "9pm", "open_sunday": true}
curl -X POST https://api.agenticcalling.ai/v1/calls \
-H "Authorization: Bearer ac_live_..." \
-H "Content-Type: application/json" \
-d '{
"to": "+14155551234",
"objective": "Ask about their business hours"
}'
Phone Numbers
A US phone number is automatically provisioned when you create your account — no setup needed. Need more numbers? Provision via the API:
line = client.lines.create(country="US")
print(line.phone_number) # +14155550142
Free accounts get 1 phone line. Pro and above get unlimited lines across 40+ countries.
Authentication
API keys and Bearer token authentication.
API Keys
All API requests require a Bearer token in the Authorization header. API keys are prefixed with ac_live_ or ac_test_.
Authorization: Bearer ac_live_7f8a9b2c...
Getting a Key
There are two ways to obtain an API key:
- Dashboard — Sign up at agenticcalling.ai/dashboard and create a key from the API Keys page.
- Register endpoint —
POST /v1/auth/registercreates an organization and returns a one-time API key.
Key Management
Each organization can have multiple active API keys. Use GET /v1/auth/api-keys to list them, POST /v1/auth/api-keys to create new ones, and DELETE /v1/auth/api-keys/{key_id} to revoke.
Usage Warning Headers
Every API response may include an X-AC-Usage-Warning header when usage is high:
| Threshold | Header Value | Action |
|---|---|---|
| 80% used | approaching_limit | Warn user proactively |
| 95% used | critical_limit | Strong warning, encourage upgrade |
| 100% | N/A | API returns 402 on new calls |
Core Concepts
Understand how AgenticCalling works under the hood.
Resource Hierarchy
Organization (top-level container)
├── PhoneLine (+1-415-555-0142)
│ ├── Call (outbound to +1-415-555-1234)
│ │ ├── Recording (audio file)
│ │ └── Transcript (structured text + extracted data)
│ ├── Conversation (groups related calls)
│ └── SMS Thread (text messages — coming soon)
├── Playbook (reusable call strategies)
├── Persona (reusable caller identities)
└── Pod (multi-tenant isolation)
Calls
A Call is an individual phone conversation. Every call has an objective (what to accomplish), follows a lifecycle managed by the platform, and produces a recording, transcript, and structured data.
Call Lifecycle
| Status | Description | Terminal |
|---|---|---|
queued | Created, waiting to be placed | No |
scheduled | Set for a future time | No |
dialing | Phone is ringing | No |
connected | Human answered | No |
in_progress | Active conversation | No |
completed | Finished successfully, data extracted | Yes |
needs_follow_up | Callback requested by recipient | No |
voicemail | Voicemail detected | No |
no_answer | Rang, nobody answered | No |
busy | Line occupied | No |
retry_scheduled | Retry queued with delay | No |
exhausted | All retries failed | Yes |
failed | Unrecoverable error | Yes |
cancelled | Cancelled by user or system | Yes |
Objectives
The objective tells the call LLM what to accomplish. It should be specific and outcome-oriented:
- Good: "Book a table for 6 at 8pm this Friday. If 8pm unavailable, anything between 7:30-9pm works. Name: Alex Chen."
- Bad: "Make a reservation"
Playbooks
A Playbook is a reusable conversation strategy. Different call types (negotiation, reservation, outreach) use fundamentally different approaches. See the Playbooks section for details.
Personas
A Persona is a reusable caller identity — name, company, role, voice, and default behavior rules. See Personas.
Data Extraction
After every call, the platform extracts structured JSON data from the transcript automatically. You can define custom extraction schemas in playbooks. See Data Extraction.
Pods
A Pod provides multi-tenant isolation for SaaS platforms building on AgenticCalling. All resources within a Pod are invisible to other Pods.
call = client.calls.create(
to="+14155551234",
objective="...",
pod_id="customer_acme" # Isolated to this tenant
)
Calls API
Create, retrieve, and manage phone calls.
Base URL: https://api.agenticcalling.ai/v1
Creates a new outbound phone call. The platform queues the call, selects a phone line, and begins dialing. Returns the call object immediately with status queued.
Auth Required
Request Body
| Parameter | Type | Description |
|---|---|---|
| to | string | Required Destination phone number in E.164 format (e.g. +14155551234) |
| objective | string | Required What the agent should accomplish on the call (max 2000 chars) |
| context | string | Optional Background info for the agent (max 4000 chars) |
| playbook_id | string | Optional Playbook to use. Auto-classified if omitted. |
| persona_id | string | Optional Caller identity to use |
| voice | string | Optional Voice preset, e.g. professional_male_en |
| language | string | Optional ISO code or "auto". Default: "en" |
| behavior_rules | string[] | Optional Hard constraints enforced during conversation (max 20) |
| on_voicemail | string | Optional leave_message | hang_up | retry. Default: leave_message |
| on_screening | string | Optional navigate | hang_up. Default: navigate |
| max_duration_minutes | integer | Optional Hard cap 1-120. Default: 30 |
| schedule_at | string | Optional ISO-8601 datetime for future scheduling |
| retry_policy | object | Optional Custom retry configuration (see below) |
| webhook_url | string | Optional Per-call webhook URL |
| from_line_id | string | Optional Specific PhoneLine to use as caller ID |
| pod_id | string | Optional Multi-tenant pod isolation |
| client_id | string | Optional Idempotency key (max 255 chars). Safe to retry. |
| voice_gender | string | Optional female (default) | male. Ignored if voice is set explicitly. |
| previous_call_context | string | Optional Summary from a prior call. Agent will reference it naturally ("Last time we spoke..."). |
| pipeline | string | Optional gemini (default, Gemini 3.1 Flash Live — lowest latency) | pipeline (OpenAI STT+LLM+TTS — best transcripts) | realtime (OpenAI Realtime API) |
Retry Policy Object
| Field | Type | Default |
|---|---|---|
| max_attempts | integer (1-10) | 3 |
| backoff | int[] (minutes) | [15, 120] |
| business_hours_only | boolean | false |
| timezone | string | America/New_York |
| on_no_answer | string | retry |
| on_busy | string | retry |
| on_voicemail | string | leave_message |
| expires_at | string | null |
curl -X POST https://api.agenticcalling.ai/v1/calls \
-H "Authorization: Bearer ac_live_..." \
-H "Content-Type: application/json" \
-d '{
"to": "+14155551234",
"objective": "Get bulk pricing for 500 steel beams",
"context": "We are Acme Corp. Budget is $50-60 per unit.",
"voice": "professional_male_en",
"behavior_rules": ["Never agree to price above $60/unit"],
"client_id": "steel-inquiry-v1"
}'
201 Created{
"id": "01HXYZ...",
"organization_id": "01HABC...",
"to_number": "+14155551234",
"objective": "Get bulk pricing for 500 steel beams",
"status": "queued",
"attempt_number": 1,
"max_attempts": 3,
"language": "en",
"max_duration_minutes": 30,
"structured_data": null,
"summary": null,
"duration_seconds": null,
"cost": null,
"created_at": "2026-03-26T10:00:00Z",
"updated_at": "2026-03-26T10:00:00Z"
}
client_id and a call with that ID already exists, the existing call is returned with status 200 instead of creating a duplicate.
Returns the full call object including status, structured data, summary, transcript, and recording URL (when available).
Auth Required
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| call_id | string | Required The call ID |
curl https://api.agenticcalling.ai/v1/calls/01HXYZ... \
-H "Authorization: Bearer ac_live_..."
200 OK{
"id": "01HXYZ...",
"status": "completed",
"duration_seconds": 252,
"cost": 0.42,
"attempt_number": 2,
"structured_data": {
"quoted_price": 52.00,
"currency": "USD",
"moq": 100,
"lead_time_days": 14,
"payment_terms": "Net 30",
"contact_name": "Mike Chen"
},
"summary": "Supplier quoted $52/unit, 14-day lead time, Net 30 terms.",
"recording_url": "/v1/calls/01HXYZ.../recording",
"transcript": [
{"speaker": "agent", "text": "Hi, this is Alex from Acme Corp..."},
{"speaker": "human", "text": "Sure, how many units?"}
]
}
Returns a paginated list of calls for your organization. Use the X-Next-Page-Token response header for cursor-based pagination.
Auth Required
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| status | string | Optional Filter by status |
| limit | integer | Optional 1-100, default 20 |
| page_token | string | Optional Cursor from previous response |
curl "https://api.agenticcalling.ai/v1/calls?status=completed&limit=10" \
-H "Authorization: Bearer ac_live_..."
200 OK// Array of call objects
[
{ "id": "01HXYZ...", "status": "completed", ... },
{ "id": "01HABC...", "status": "completed", ... }
]
// Header: X-Next-Page-Token: eyJ...
Creates multiple calls concurrently. The platform manages parallel dialing, retries, and result aggregation. Up to 1000 targets per batch.
Auth Required
Request Body
| Parameter | Type | Description |
|---|---|---|
| targets | object[] | Required Array of {to, objective?, context?, client_id?} |
| objective | string | Required Default objective for all targets |
| playbook_id | string | Optional Playbook to apply to all calls |
| persona_id | string | Optional Persona for all calls |
| webhook_url | string | Optional Webhook for all batch events |
| pod_id | string | Optional Pod isolation |
curl -X POST https://api.agenticcalling.ai/v1/calls/batch \
-H "Authorization: Bearer ac_live_..." \
-H "Content-Type: application/json" \
-d '{
"targets": [
{"to": "+14155551111", "context": "Shenzhen Metals Co"},
{"to": "+14155552222", "context": "Guangzhou Supply Ltd"},
{"to": "+14155553333", "context": "Dongguan PhoneTech"}
],
"objective": "Get steel beam pricing quote for 500 units",
"playbook_id": "platform.price_inquiry"
}'
201 Created{
"batch_id": "batch_01HXYZ...",
"total": 3,
"calls": [
{ "id": "01H...", "status": "queued", "to_number": "+14155551111" },
{ "id": "01H...", "status": "queued", "to_number": "+14155552222" },
{ "id": "01H...", "status": "queued", "to_number": "+14155553333" }
]
}
Returns the status of a batch call with per-call results and aggregated status counts.
Auth Required
curl https://api.agenticcalling.ai/v1/calls/batch/batch_01HXYZ... \
-H "Authorization: Bearer ac_live_..."
200 OK{
"batch_id": "batch_01HXYZ...",
"total": 3,
"statuses": { "completed": 2, "exhausted": 1 },
"calls": [ /* array of full call objects */ ]
}
Returns a 302 redirect to a signed S3 URL for the full transcript file. The URL expires in 1 hour.
Auth Required
curl -L https://api.agenticcalling.ai/v1/calls/01HXYZ.../transcript \
-H "Authorization: Bearer ac_live_..."
Returns 404 if the transcript is not yet available.
Returns a 302 redirect to a signed S3 URL for the audio recording. The URL expires in 1 hour.
Auth Required
curl -L https://api.agenticcalling.ai/v1/calls/01HXYZ.../recording \
-H "Authorization: Bearer ac_live_..."
Returns 404 if the recording is not yet available.
Cancels a queued or scheduled call. Returns 409 if the call is already in progress or completed.
Auth Required
curl -X DELETE https://api.agenticcalling.ai/v1/calls/01HXYZ.../cancel \
-H "Authorization: Bearer ac_live_..."
200 OK{
"id": "01HXYZ...",
"status": "cancelled",
...
}
connected or in_progress. If balance runs out mid-call, the platform completes the call gracefully.
Auth & API Keys
Registration, API key management, and billing.
Base URL: https://api.agenticcalling.ai/v1
Creates a new organization and returns a one-time API key. If an org with the same email already exists, a new key is generated for the existing org.
No authentication required.
Request Body
| Parameter | Type | Description |
|---|---|---|
| name | string | Required Organization name |
| string | Required Contact email |
curl -X POST https://api.agenticcalling.ai/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corp", "email": "dev@acme.com"}'
201 Created{
"org_id": "01HABC...",
"api_key": "ac_live_7f8a9b2c...",
"message": "Store this key — it won't be shown again"
}
Lists all active API keys for the authenticated organization.
Auth Required
200 OK[
{
"id": "01HKEY...",
"name": "Production",
"key_prefix": "ac_live_",
"is_active": true,
"created_at": "2026-03-01T00:00:00Z",
"last_used_at": "2026-03-26T12:00:00Z"
}
]
Generates a new API key for the authenticated organization. The full key is returned only once.
Auth Required
Request Body
| Parameter | Type | Description |
|---|---|---|
| name | string | Optional A label for this key |
201 Created{
"id": "01HKEY...",
"api_key": "ac_live_...",
"name": "Production",
"message": "Store this key — it won't be shown again"
}
Soft-deletes an API key by setting it inactive. Returns 204 No Content on success.
Auth Required
curl -X DELETE https://api.agenticcalling.ai/v1/auth/api-keys/01HKEY... \
-H "Authorization: Bearer ac_live_..."
Update organization settings such as BYOK (Bring Your Own Key) OpenAI API key.
Auth Required
Request Body
| Parameter | Type | Description |
|---|---|---|
| openai_api_key | string | Optional Your OpenAI API key (empty string clears it) |
200 OK{ "updated": true }
Creates a Stripe Billing Portal session and returns the URL. Redirect the user to manage their subscription.
Auth Required
200 OK{ "url": "https://billing.stripe.com/session/..." }
Creates a Stripe Checkout session for a subscription upgrade.
Auth Required
Request Body
| Parameter | Type | Description |
|---|---|---|
| price_id | string | Required Stripe price ID |
| success_url | string | Required Redirect URL on success |
| cancel_url | string | Required Redirect URL on cancel |
200 OK{ "url": "https://checkout.stripe.com/..." }
Usage & Billing
Monitor your usage, billing, and plan limits.
Returns current usage, plan limits, remaining balance, and cost breakdown for the authenticated organization.
Auth Required
curl https://api.agenticcalling.ai/v1/usage \
-H "Authorization: Bearer ac_live_..."
200 OK{
"organization_id": "01HABC...",
"plan": "free",
"minutes_used_this_month": 3.5,
"minutes_limit": 5.0,
"minutes_remaining": 1.5,
"sms_sent_this_month": 4,
"sms_limit": 10,
"sms_remaining": 6,
"billing_reset_at": "2026-04-01T00:00:00Z",
"concurrent_calls_limit": 3,
"cost": {
"total_spend_usd": 4.20,
"total_calls_billed": 15,
"total_seconds_billed": 1470,
"rates_per_pipeline": {
"gemini": { "platform_per_min": 0.036, "llm_per_min": 0.006, "total_per_min": 0.042 },
"pipeline": { "platform_per_min": 0.036, "llm_per_min": 0.018, "total_per_min": 0.054 },
"realtime": { "platform_per_min": 0.036, "llm_per_min": 0.300, "total_per_min": 0.336 }
},
"sms_rate_per_message_usd": 0.0095
}
}
GET /v1/usage first and warn the user.
What Counts as Usage
| Resource | Billing Rule |
|---|---|
| Call minutes | Per-second from when the call connects (human or voicemail answers). Ringing is free. No-answer/busy = free. |
| SMS | Per message segment, outbound and inbound counted separately |
| Recording storage | Cumulative across all recordings, auto-purge per retention policy |
Balance Behavior
| Scenario | Behavior |
|---|---|
| Before a call | API returns 402 Payment Required with upgrade URL |
| During active call | Never cut off. Call completes gracefully. Overage absorbed. |
| During batch | Active calls complete. Remaining queued calls cancelled. |
| Scheduled retries | Paused (not cancelled). Resume when balance restored. |
Playbooks
Reusable conversation strategies for different types of calls.
A Playbook defines how a call should be conducted. Different call types require different approaches, extraction schemas, and success criteria.
Platform Playbooks (built-in)
Pre-built strategies that ship with the platform. Auto-selected based on the objective when no playbook is specified.
| Playbook ID | Use Case | Strategy |
|---|---|---|
platform.reservation | Book tables, appointments, rooms | Efficient, transactional |
platform.price_inquiry | Get quotes, rates, availability | Exploratory, asks about pricing/MOQ/terms |
platform.negotiation | Haggle, counter-offer, anchor | Strategic, anchors low, counter-offers |
platform.information_gather | Surveys, interviews, research | Open-ended, neutral |
platform.scheduling | Move/cancel/confirm appointments | Direct, confirms new details |
platform.cold_outreach | Sales calls, lead generation | Persuasive, qualifies and pitches |
platform.complaint | Escalate issues, get resolutions | Assertive, requests resolution |
platform.follow_up | Check status, nudge responses | Polite, references previous interaction |
platform.general | Fallback for anything else | Adaptive, follows objective literally |
Custom Playbooks
Fork a platform playbook or create from scratch:
playbook = client.playbooks.create(
name="Steel beam negotiation",
base="platform.negotiation",
strategy="""
Build rapport first. Our target is $50/unit for 500 units.
Walk-away ceiling is $58. We have a competing quote at $53.
Always ask for bulk discount and payment terms.
""",
behavior_rules=[
"Never agree to price above $58/unit",
"Never disclose our current supplier",
"Always ask for a written quote",
],
extraction_schema={
"quoted_price": "number",
"currency": "string",
"moq": "number",
"lead_time_days": "number",
"payment_terms": "string",
},
voice_tone="Warm, professional, never pushy."
)
Auto-Classification
When no playbook_id is passed, the platform analyzes the objective and auto-selects the best playbook:
# No playbook specified — platform figures it out
call = client.calls.create(
to="+14155551234",
objective="Book a table for 4 at 7pm Saturday"
)
# Auto-selects platform.reservation
Playbook API
# Create
playbook = client.playbooks.create(name, strategy, base?, behavior_rules?, extraction_schema?, voice_tone?, success_criteria?)
# List
playbooks = client.playbooks.list()
# Update
client.playbooks.update(playbook_id, strategy?, behavior_rules?, extraction_schema?)
# Delete
client.playbooks.delete(playbook_id)
Creating Playbooks via Claude or ChatGPT
If you're using AgenticCalling through Claude or ChatGPT (via MCP), you don't need to write any code. Just ask conversationally:
- "Create a playbook called 'Hotel Negotiation' that starts by asking for available dates, then negotiates the rate down by mentioning competitor prices"
- "Set up a playbook for supplier outreach — always ask about MOQ, lead time, and bulk discounts. Never commit above $50/unit."
- "Make a customer survey playbook that asks 5 questions about product satisfaction and extracts a 1-10 rating for each"
Claude/ChatGPT will use the create_playbook tool automatically. The playbook is saved to your account and can be reused across calls.
Personas
Reusable caller identities for consistent representation.
A Persona is a reusable caller identity — name, company, role, voice, and default behavior rules. Instead of specifying who the agent should be on every call, create a Persona once and reference it.
persona = client.personas.create(
name="Procurement Agent",
caller_name="Alex",
company="Acme Corp",
role="Procurement specialist",
voice="professional_male_en",
language="en",
default_rules=[
"Always identify yourself as Alex from Acme Corp",
"Never commit to purchases over $10,000 without approval",
]
)
# Use in calls
call = client.calls.create(
to="+14155551234",
persona_id=persona.persona_id,
objective="Get steel beam pricing"
)
# Agent introduces itself as "Alex from Acme Corp"
Persona API
personas.create(name, caller_name, company?, role?, voice?, language?, default_rules?, client_id?)
personas.list(limit?, page_token?)
personas.get(persona_id)
personas.update(persona_id, caller_name?, company?, voice?, default_rules?)
personas.delete(persona_id)
Creating Personas via Claude or ChatGPT
No code needed — just describe who you want the caller to be:
- "Create a persona called 'Sarah from Procurement' — professional tone, works at Acme Corp, speaks with a warm but direct communication style"
- "Set up a persona for cold outreach — casual and friendly, introduces as Alex from TechStartup, keep it brief and energetic"
- "Make a customer service persona that's patient, empathetic, and always confirms details before ending the call"
Your AI uses the create_persona tool to save it. Use the persona in any future call by referencing its name.
Phone Numbers
Your AI agent's caller ID for outbound calls.
Automatic Provisioning
When you create your account, a US phone number is automatically provisioned for you — no setup required. This number is used as the caller ID for all outbound calls your AI agent makes.
Plan Limits
| Plan | Phone Lines | Cost per Additional Line |
|---|---|---|
| Free | 1 | — |
| Pro | Unlimited | $2/month each |
| Business | Unlimited | $1.50/month each |
Provisioning Additional Lines
Need more numbers? Provision them via the API or ask Claude/ChatGPT:
curl -X POST https://api.agenticcalling.ai/v1/lines \
-H "Authorization: Bearer ac_live_..." \
-H "Content-Type: application/json" \
-d '{"country": "US"}'
Response
{
"id": "line_abc123",
"phone_number": "+14155550142",
"country": "US",
"created_at": "2026-03-26T12:00:00Z"
}
Using a Specific Line
By default, the platform selects the best available line. To use a specific number as caller ID:
curl -X POST https://api.agenticcalling.ai/v1/calls \
-H "Authorization: Bearer ac_live_..." \
-H "Content-Type: application/json" \
-d '{
"to": "+14155551234",
"objective": "Schedule a demo",
"from_line_id": "line_abc123"
}'
Inbound Calls
Coming soon. Inbound call handling — where someone calls your AI agent's phone number — is currently in development.
Structured Data Extraction
Automatically extract structured JSON data from every conversation.
After every call, the platform runs an extraction pass on the full transcript using the playbook's extraction_schema. This produces clean, structured JSON that agents can immediately use for comparison, storage, or follow-up actions.
How It Works
- Call completes and transcript is finalized
- Platform runs extraction using the playbook's schema (or auto-generates one from the objective)
- Structured data is attached to the call response in
structured_data - A natural-language
summaryis also generated
Defining Extraction Schemas
playbook = client.playbooks.create(
name="Supplier quote",
strategy="Get pricing details for our order",
extraction_schema={
"quoted_price": "number",
"currency": "string",
"moq": "number",
"lead_time_days": "number",
"payment_terms": "string",
"contact_name": "string",
"willingness_to_negotiate": "high/medium/low",
"next_steps": "string"
}
)
Example Output
// result.structured_data
{
"quoted_price": 52.00,
"currency": "USD",
"moq": 100,
"lead_time_days": 14,
"payment_terms": "Net 30",
"contact_name": "Mike Chen",
"willingness_to_negotiate": "medium",
"next_steps": "Send PO to mike@supplier.com"
}
// result.summary
"Supplier quoted $52/unit for 500 steel beams, 14-day lead time, Net 30 terms. Contact: Mike Chen. Willing to negotiate on bulk orders."
extraction_schema is defined, the platform infers one from the objective and extracts whatever structured data is relevant.
Webhooks
Receive real-time notifications for call events.
Setup
webhook = client.webhooks.create(
url="https://myapp.com/webhooks/agenticcalling",
event_types=["call.completed", "call.exhausted", "call.dnc_requested"],
client_id="my-webhook-v1"
)
# webhook.secret = "whsec_..." (for signature verification)
You can also set a per-call webhook:
call = client.calls.create(
to="+14155551234",
objective="...",
webhook_url="https://myapp.com/webhooks/specific-call"
)
Call Events
| Event | When |
|---|---|
call.queued | Call created and queued for dialing |
call.attempting | Platform dials (including retries) |
call.connected | Human answered |
call.completed | Finished successfully, includes structured_data and summary |
call.attempt_failed | Single attempt failed (no_answer, busy, etc.) |
call.needs_follow_up | Callback requested by recipient |
call.exhausted | All retries failed (terminal) |
call.callback_received | Someone called back your PhoneLine (Inbound calls coming soon) |
call.channel_switched | Recipient asked to switch to SMS/email |
call.dnc_requested | Recipient requested do-not-call |
call.failed | Unrecoverable error (terminal) |
SMS Events
| Event | When |
|---|---|
sms.sent | Outbound SMS delivered |
sms.received | Inbound SMS received on a PhoneLine |
sms.schedule_extracted | Inbound SMS contained scheduling info |
sms.dnc_detected | Inbound SMS contained opt-out language |
Billing Events
| Event | When |
|---|---|
balance.warning | Usage reached 80% or 95% of limit |
Example: call.completed Payload
{
"event_type": "call.completed",
"event_id": "evt_01HXYZ...",
"call": {
"call_id": "call_01HXYZ...",
"status": "completed",
"duration_seconds": 252,
"cost": 0.42,
"attempts": 2,
"structured_data": {
"quoted_price": 52,
"lead_time_days": 14
},
"summary": "Supplier quoted $52/unit, 14-day lead time",
"transcript_url": "/v1/calls/call_.../transcript",
"recording_url": "/v1/calls/call_.../recording"
}
}
Verifying Signatures
Every webhook includes an X-AC-Signature header (HMAC-SHA256). Verify it with your webhook secret:
import hmac, hashlib
def verify_signature(headers, body, secret):
expected = hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(
headers.get("X-AC-Signature", ""), expected
)
Delivery & Retries
- Your endpoint must return
200 OKwithin 10 seconds - Failed deliveries retried 3 times: 1 min, 5 min, 30 min
- Events stored for 7 days if all deliveries fail
WebSockets
Real-time streaming for live transcripts and call status updates.
WebSockets provide a persistent connection for real-time events during active calls. Build live dashboards and stream transcripts.
Connecting
const ws = new WebSocket(
"wss://api.agenticcalling.ai/v1/ws?token=ac_live_..."
);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data.event_type, data);
};
Filtering
// Stream events for a specific call
"wss://api.agenticcalling.ai/v1/ws?token=ac_...&call_id=call_01HXYZ"
// Stream events for a batch
"wss://api.agenticcalling.ai/v1/ws?token=ac_...&batch_id=batch_01HXYZ"
// Stream events for a PhoneLine
"wss://api.agenticcalling.ai/v1/ws?token=ac_...&line_id=line_01HXYZ"
Live Transcript Events
{"event_type": "transcript.partial", "call_id": "call_...", "speaker": "agent", "text": "Hi, this is Alex from Acme."}
{"event_type": "transcript.partial", "call_id": "call_...", "speaker": "human", "text": "How can I help you?"}
When to Use WebSockets vs Webhooks
| Use Case | WebSockets | Webhooks |
|---|---|---|
| Live dashboard / transcript display | Yes | No |
| Backend processing of completed calls | No | Yes |
| Agent integration (Claude, OpenClaw) | No | Yes |
| No public URL available | Yes | No |
MCP Integration
Connect AgenticCalling to Claude, ChatGPT, Cursor, and other AI agents.
MCP Server Setup
MCP (Model Context Protocol) is an open standard that enables AI applications to connect with external tools. Add this to your MCP client configuration:
{
"mcpServers": {
"AgenticCalling": {
"command": "npx",
"args": ["-y", "agenticcalling-mcp"],
"env": {
"AC_API_KEY": "YOUR_API_KEY"
}
}
}
}
{
"mcpServers": {
"AgenticCalling": {
"command": "npx",
"args": ["-y", "agenticcalling-mcp", "--tools", "make_call,get_call_status"],
"env": { "AC_API_KEY": "YOUR_API_KEY" }
}
}
}
Available MCP Tools
| Tool | Description |
|---|---|
make_call | Make a phone call with objective, context, voice, language, behavior_rules |
make_batch_calls | Call multiple numbers concurrently (agent swarm) |
get_call_status | Check status, get transcript and structured data |
list_calls | List recent calls with statuses and results |
create_playbook | Create/save a reusable call strategy |
list_playbooks | List available playbooks |
create_persona | Create a reusable caller identity (name, company, role, voice, rules) |
list_personas | List all saved personas |
send_sms | Send a text message from agent's phone number (Coming Soon) |
create_phone_line | Provision a new phone number |
list_phone_lines | List all provisioned numbers |
get_usage | Check current usage and remaining balance |
Claude.ai (Web / Mobile)
- Go to Claude Settings → Integrations → Add MCP
- Enter MCP server URL:
https://mcp.agenticcalling.ai/sse - Enter your API key when prompted
- Done. Claude now has calling tools.
Skills Installation
npx skills add autoacquire/agenticcalling-skills --skill agenticcalling --agent claude-code
npx skills add autoacquire/agenticcalling-skills --skill agenticcalling --agent cursor
npx clawhub@latest install agenticcalling
Then configure in ~/.openclaw/openclaw.json:
{
"skills": {
"entries": {
"agenticcalling": {
"enabled": true,
"env": { "AC_API_KEY": "your-api-key-here" }
}
}
}
}
Docs for Agents
Give your agent current docs in three formats:
| Format | URL |
|---|---|
| Documentation index | https://docs.agenticcalling.ai/llms.txt |
| Complete docs (one file) | https://docs.agenticcalling.ai/llms-full.txt |
| Per-page markdown | https://docs.agenticcalling.ai/quickstart.md |
SDKs
Official client libraries for AgenticCalling.
Python SDK
pip install agenticcalling python-dotenv
import os
from dotenv import load_dotenv
from agenticcalling import AgenticCalling
load_dotenv()
client = AgenticCalling(api_key=os.getenv("AC_API_KEY"))
# Make a call
call = client.calls.create(
to="+14155551234",
objective="Ask about bulk pricing for 500 steel beams",
context="We are Acme Corp. Budget is $50-60 per unit.",
voice="professional_male_en",
client_id="steel-inquiry-v1"
)
# Get results
result = client.calls.get(call.call_id)
print(result.structured_data)
# Batch calls
batch = client.calls.create_batch(
targets=[
{"to": "+14155551111", "context": "Supplier A"},
{"to": "+14155552222", "context": "Supplier B"},
],
objective="Get steel beam pricing",
playbook_id="platform.price_inquiry"
)
# Check usage
usage = client.usage.get()
print(f"{usage.minutes_remaining} minutes remaining")
# Send SMS (Coming Soon)
# sms = client.sms.send(
# to="+14155551234",
# message="Following up on our call about steel pricing."
# )
REST API (cURL)
The REST API works with any HTTP client. No SDK required.
curl -X POST https://api.agenticcalling.ai/v1/calls \
-H "Authorization: Bearer ac_live_..." \
-H "Content-Type: application/json" \
-d '{"to": "+14155551234", "objective": "Ask about business hours"}'
Node.js / TypeScript SDK
npm install @agenticcalling/sdk # Coming soon
Pricing & Rate Limits
Usage-based pricing with a generous free tier.
Per-Minute Rates by Pipeline
Pricing depends on which voice AI pipeline you use. Platform costs (telephony + servers) are the same across all pipelines. LLM costs vary by model.
| Pipeline | Platform | LLM | Total per minute |
|---|---|---|---|
| Gemini 3.1 Flash (default) | $0.036 | $0.006 | $0.042/min |
| OpenAI Pipeline (STT+LLM+TTS) | $0.036 | $0.018 | $0.054/min |
| OpenAI Realtime | $0.036 | $0.300 | $0.336/min |
Platform fee breakdown: Twilio SIP $0.010 + LiveKit $0.004 + Servers $0.016 + 20% margin = $0.036/min.
Plans
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Monthly base | $0 | $29 | $99 | Custom |
| Call minutes | 5/mo included | Pay per min | Pay per min | Volume discount |
| Phone lines | 1 | Unlimited | Unlimited | Unlimited |
| SMS | Coming soon | |||
| Concurrent calls | 3 | 20 | 50 | Custom |
| Playbooks | Platform only | Custom + platform | Custom + platform | Custom + marketplace |
| Support | Community | Priority | Dedicated | |
Rate Limits
| Endpoint | Free | Pro | Business |
|---|---|---|---|
| POST /v1/calls | 10/min | 60/min | 200/min |
| POST /v1/calls/batch | 2/min | 10/min | 30/min |
| GET endpoints | 60/min | 300/min | 1000/min |
Rate-limited requests return 429 Too Many Requests with a Retry-After header.
Billing Details
Calls under 2 minutes (grace buffer) are free. Billing starts per-second when the call connects (human or voicemail answers). Rounding is to the next full minute. Ringing, no-answer, and busy signals are always free.
Every completed call stores a cost_breakdown object showing exact charges for telephony, servers, and LLM separately. Check via GET /v1/calls/{'{call_id}'}.
Error Codes
HTTP status codes and error response format.
Error Response Format
{
"detail": "Description of what went wrong"
}
For 402 errors, a structured body is returned:
{
"error": {
"code": "insufficient_balance",
"message": "You've used all 5 free minutes this month.",
"details": {
"plan": "free",
"minutes_used": 5.0,
"minutes_limit": 5.0,
"resets_at": "2026-04-01T00:00:00Z",
"upgrade_url": "https://agenticcalling.ai/dashboard/upgrade"
},
"suggestions": [
"Upgrade to Pro for unlimited calls",
"Wait until April 1 for free tier reset"
]
}
}
Status Codes
| Code | Name | Description |
|---|---|---|
200 | OK | Request succeeded |
201 | Created | Resource created successfully |
204 | No Content | Successful deletion |
302 | Found | Redirect to signed URL (transcripts, recordings) |
401 | Unauthorized | Missing or invalid API key |
402 | Payment Required | Insufficient balance. Includes upgrade URL and usage details. |
404 | Not Found | Resource not found (call, key, transcript not ready) |
409 | Conflict | Cannot cancel a call in its current state |
422 | Unprocessable Entity | Validation error or DNC blocked number |
429 | Too Many Requests | Rate limited. Check Retry-After header. |
502 | Bad Gateway | Failed to generate signed URL for recording/transcript |
503 | Service Unavailable | External service not configured (e.g., Stripe) |
SDK Exception Classes
| Exception | HTTP Code | Properties |
|---|---|---|
AuthenticationError | 401 | Invalid API key |
InsufficientBalanceError | 402 | .minutes_used, .minutes_limit, .resets_at, .upgrade_url |
NotFoundError | 404 | Resource not found |
ValidationError | 422 | Bad parameters |
RateLimitError | 429 | .retry_after (seconds) |
AgenticCallingError | 5xx | Server error |
Handling Errors (Python SDK)
from agenticcalling.exceptions import (
InsufficientBalanceError,
RateLimitError,
AuthenticationError,
)
try:
call = client.calls.create(to="+14155551234", objective="...")
except InsufficientBalanceError as e:
print(f"Out of minutes: {e.minutes_used}/{e.minutes_limit} used")
print(f"Resets: {e.resets_at}")
print(f"Upgrade: {e.upgrade_url}")
except RateLimitError as e:
time.sleep(e.retry_after or 60)
except AuthenticationError:
print("Invalid API key. Check AC_API_KEY.")