Quick Start
Make your first AI phone call in 5 minutes.
1. Get an API Key
Sign up at agenticcalling.ai/dashboard and create an API key. Free tier includes 30 minutes/month, 3 phone lines, and 50 SMS.
Phone Numbers
A US phone number is automatically provisioned when you create your account — no setup needed. Your AI agent uses this number as the caller ID for all outbound calls and can receive inbound calls and SMS on it.
Need more numbers? You can provision additional phone lines via the API:
# Provision an additional US number
line = client.lines.create(country="US")
print(line.phone_number) # +14155550142
# Use a specific line for a call
call = client.calls.create(
to="+14155551234",
objective="Schedule a demo",
from_line_id=line.id
)
Free accounts get up to 3 phone lines. Pro and above get unlimited lines across 40+ countries.
2. Install the SDK
pip install agenticcalling python-dotenv
npm install @agenticcalling/sdk
3. Make Your First Call
import os
from dotenv import load_dotenv
from agenticcalling import AgenticCalling
load_dotenv()
client = AgenticCalling(api_key=os.getenv("AC_API_KEY"))
# A US phone number is auto-provisioned on signup
call = client.calls.create(
to="+14155551234",
objective="Ask what time they close today"
)
print(f"Call ID: {call.call_id}")
print(f"Status: {call.status}") # queued
4. Or Use cURL Directly
curl -X POST https://api.agenticcalling.ai/v1/calls \
-H "Authorization: Bearer ac_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"to": "+14155551234",
"objective": "Ask about their business hours"
}'
5. Check the Results
import time
# Poll for completion (or use webhooks for production)
while True:
result = client.calls.get(call.call_id)
if result.status in ["completed", "exhausted", "failed"]:
break
time.sleep(10)
if result.status == "completed":
print(f"Duration: {result.duration_seconds}s")
print(f"Summary: {result.summary}")
print(f"Data: {result.structured_data}")
# e.g. {"closing_time": "9pm", "open_sunday": true}
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_sk_ or ac_test_sk_.
Authorization: Bearer ac_live_sk_7f8a9b2c...
Getting a Key
There are three 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. - OTP flow — Use
POST /v1/auth/otp/sendandPOST /v1/auth/otp/verifyfor email-based authentication (used by MCP connectors).
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)
├── 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. |
| pipeline | string | Optional realtime | pipeline. Default: pipeline |
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_sk_..." \
-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_sk_..."
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_sk_..."
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_sk_..." \
-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_sk_..."
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_sk_..."
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_sk_..."
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_sk_..."
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_sk_7f8a9b2c...",
"message": "Store this key — it won't be shown again"
}
Sends a 6-digit verification code to the given email address. Codes expire in 10 minutes. Rate limited to 1 OTP per email per 60 seconds.
No authentication required. Used by MCP connectors for email-based auth.
Request Body
| Parameter | Type | Description |
|---|---|---|
| string | Required Email address to send OTP to |
curl -X POST https://api.agenticcalling.ai/v1/auth/otp/send \
-H "Content-Type: application/json" \
-d '{"email": "dev@acme.com"}'
200 OK{ "sent": true, "email": "dev@acme.com" }
Verifies the OTP and returns an API key. Creates a new organization if the email is not registered, or generates a new key for the existing org.
Request Body
| Parameter | Type | Description |
|---|---|---|
| string | Required Email used in /otp/send | |
| otp | string | Required 6-digit code received via email |
200 OK{
"api_key": "ac_live_sk_...",
"org_id": "01HABC...",
"is_new": false
}
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_sk_...",
"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_sk_..."
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_sk_..."
200 OK{
"organization_id": "01HABC...",
"plan": "free",
"minutes_used_this_month": 24.5,
"minutes_limit": 30.0,
"minutes_remaining": 5.5,
"sms_sent_this_month": 12,
"sms_limit": 50,
"sms_remaining": 38,
"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,
"rate_per_minute_usd": 0.1714,
"sms_rate_per_message_usd": 0.013
}
}
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)
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)
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 |
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_sk_..."
);
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,send_sms"],
"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 |
send_sms | Send a text message from agent's phone number |
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
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_sk_..." \
-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.
Plans
| Free | Pro | Business | Enterprise | |
|---|---|---|---|---|
| Monthly base | $0 | $29 | $99 | Custom |
| US call minutes | 30/mo included | $0.10/min | $0.08/min | Volume discount |
| International | — | $0.15-0.40/min | $0.12-0.35/min | Volume discount |
| Phone lines | 3 | Unlimited | Unlimited | Unlimited |
| SMS | 50/mo | $0.01/outbound | $0.008/outbound | Volume |
| Concurrent calls | 3 | 20 | 50 | Custom |
| Recording storage | 1 GB (30 days) | 10 GB (90 days) | 50 GB (1 year) | Custom |
| Playbooks | Platform only | Custom + platform | Custom + platform | Custom + marketplace |
| Pods | — | — | Yes | Yes |
| 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 |
| POST /v1/auth/otp/send | 1 per email per 60 seconds | ||
Rate-limited requests return 429 Too Many Requests with a Retry-After header.
Cost Breakdown
Each call minute includes telephony, voice AI, and real-time media processing — all bundled into a single per-minute rate. No separate charges for individual components.
Calls under 2 minutes (grace buffer) are free. Billing starts per-second when the call connects (human or voicemail answers). Ringing, no-answer, and busy signals are always free.
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 30 free minutes this month.",
"details": {
"plan": "free",
"minutes_used": 30.0,
"minutes_limit": 30.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.")