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

Python
pip install agenticcalling python-dotenv
TypeScript (coming soon)
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}
How It Works Your agent sends an objective via the API. AgenticCalling handles the entire phone conversation — dialing, speaking, listening, navigating IVR menus, handling voicemail — and returns structured results. Your agent never participates in the call directly; it just gets back the data.

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:

  1. Dashboard — Sign up at agenticcalling.ai/dashboard and create a key from the API Keys page.
  2. Register endpointPOST /v1/auth/register creates an organization and returns a one-time API key.
  3. OTP flow — Use POST /v1/auth/otp/send and POST /v1/auth/otp/verify for email-based authentication (used by MCP connectors).
Store your key securely API keys are shown only once at creation time. Store them in environment variables or a secrets manager. Never commit keys to source control.

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:

ThresholdHeader ValueAction
80% usedapproaching_limitWarn user proactively
95% usedcritical_limitStrong warning, encourage upgrade
100%N/AAPI 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

queued → dialing → connected → in_progress → completed ↘ no_answer → retry_scheduled → (back to dialing) ↘ busy → retry_scheduled → (back to dialing) ↘ voicemail → voicemail_left → retry_scheduled ↘ failed (invalid number, etc.) After max retries → exhausted (terminal)
StatusDescriptionTerminal
queuedCreated, waiting to be placedNo
scheduledSet for a future timeNo
dialingPhone is ringingNo
connectedHuman answeredNo
in_progressActive conversationNo
completedFinished successfully, data extractedYes
needs_follow_upCallback requested by recipientNo
voicemailVoicemail detectedNo
no_answerRang, nobody answeredNo
busyLine occupiedNo
retry_scheduledRetry queued with delayNo
exhaustedAll retries failedYes
failedUnrecoverable errorYes
cancelledCancelled by user or systemYes

Objectives

The objective tells the call LLM what to accomplish. It should be specific and outcome-oriented:

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

POST /v1/calls Create a new call

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

ParameterTypeDescription
tostringRequired Destination phone number in E.164 format (e.g. +14155551234)
objectivestringRequired What the agent should accomplish on the call (max 2000 chars)
contextstringOptional Background info for the agent (max 4000 chars)
playbook_idstringOptional Playbook to use. Auto-classified if omitted.
persona_idstringOptional Caller identity to use
voicestringOptional Voice preset, e.g. professional_male_en
languagestringOptional ISO code or "auto". Default: "en"
behavior_rulesstring[]Optional Hard constraints enforced during conversation (max 20)
on_voicemailstringOptional leave_message | hang_up | retry. Default: leave_message
on_screeningstringOptional navigate | hang_up. Default: navigate
max_duration_minutesintegerOptional Hard cap 1-120. Default: 30
schedule_atstringOptional ISO-8601 datetime for future scheduling
retry_policyobjectOptional Custom retry configuration (see below)
webhook_urlstringOptional Per-call webhook URL
from_line_idstringOptional Specific PhoneLine to use as caller ID
pod_idstringOptional Multi-tenant pod isolation
client_idstringOptional Idempotency key (max 255 chars). Safe to retry.
pipelinestringOptional realtime | pipeline. Default: pipeline

Retry Policy Object

FieldTypeDefault
max_attemptsinteger (1-10)3
backoffint[] (minutes)[15, 120]
business_hours_onlybooleanfalse
timezonestringAmerica/New_York
on_no_answerstringretry
on_busystringretry
on_voicemailstringleave_message
expires_atstringnull
Example Request
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"
  }'
Response 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"
}
Idempotency If you pass a client_id and a call with that ID already exists, the existing call is returned with status 200 instead of creating a duplicate.
GET /v1/calls/{call_id} Get call details

Returns the full call object including status, structured data, summary, transcript, and recording URL (when available).

Auth Required

Path Parameters

ParameterTypeDescription
call_idstringRequired The call ID
Example Request
curl https://api.agenticcalling.ai/v1/calls/01HXYZ... \
  -H "Authorization: Bearer ac_live_sk_..."
Response 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?"}
  ]
}
GET /v1/calls List calls

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

ParameterTypeDescription
statusstringOptional Filter by status
limitintegerOptional 1-100, default 20
page_tokenstringOptional Cursor from previous response
Example Request
curl "https://api.agenticcalling.ai/v1/calls?status=completed&limit=10" \
  -H "Authorization: Bearer ac_live_sk_..."
Response 200 OK
// Array of call objects
[
  { "id": "01HXYZ...", "status": "completed", ... },
  { "id": "01HABC...", "status": "completed", ... }
]
// Header: X-Next-Page-Token: eyJ...
POST /v1/calls/batch Create a batch of calls (agent swarm)

Creates multiple calls concurrently. The platform manages parallel dialing, retries, and result aggregation. Up to 1000 targets per batch.

Auth Required

Request Body

ParameterTypeDescription
targetsobject[]Required Array of {to, objective?, context?, client_id?}
objectivestringRequired Default objective for all targets
playbook_idstringOptional Playbook to apply to all calls
persona_idstringOptional Persona for all calls
webhook_urlstringOptional Webhook for all batch events
pod_idstringOptional Pod isolation
Example Request
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"
  }'
Response 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" }
  ]
}
GET /v1/calls/batch/{batch_id} Get batch status

Returns the status of a batch call with per-call results and aggregated status counts.

Auth Required

Example Request
curl https://api.agenticcalling.ai/v1/calls/batch/batch_01HXYZ... \
  -H "Authorization: Bearer ac_live_sk_..."
Response 200 OK
{
  "batch_id": "batch_01HXYZ...",
  "total": 3,
  "statuses": { "completed": 2, "exhausted": 1 },
  "calls": [ /* array of full call objects */ ]
}
GET /v1/calls/{call_id}/transcript Get call transcript

Returns a 302 redirect to a signed S3 URL for the full transcript file. The URL expires in 1 hour.

Auth Required

Example Request
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.

GET /v1/calls/{call_id}/recording Get call recording

Returns a 302 redirect to a signed S3 URL for the audio recording. The URL expires in 1 hour.

Auth Required

Example Request
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.

DELETE /v1/calls/{call_id}/cancel Cancel a call

Cancels a queued or scheduled call. Returns 409 if the call is already in progress or completed.

Auth Required

Example Request
curl -X DELETE https://api.agenticcalling.ai/v1/calls/01HXYZ.../cancel \
  -H "Authorization: Bearer ac_live_sk_..."
Response 200 OK
{
  "id": "01HXYZ...",
  "status": "cancelled",
  ...
}
Never cancel active calls You cannot cancel a call that is 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

POST /v1/auth/register Register a new organization

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

ParameterTypeDescription
namestringRequired Organization name
emailstringRequired Contact email
Example Request
curl -X POST https://api.agenticcalling.ai/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme Corp", "email": "dev@acme.com"}'
Response 201 Created
{
  "org_id": "01HABC...",
  "api_key": "ac_live_sk_7f8a9b2c...",
  "message": "Store this key — it won't be shown again"
}
POST /v1/auth/otp/send Send OTP email

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

ParameterTypeDescription
emailstringRequired Email address to send OTP to
Example Request
curl -X POST https://api.agenticcalling.ai/v1/auth/otp/send \
  -H "Content-Type: application/json" \
  -d '{"email": "dev@acme.com"}'
Response 200 OK
{ "sent": true, "email": "dev@acme.com" }
POST /v1/auth/otp/verify Verify OTP and get API key

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

ParameterTypeDescription
emailstringRequired Email used in /otp/send
otpstringRequired 6-digit code received via email
Response 200 OK
{
  "api_key": "ac_live_sk_...",
  "org_id": "01HABC...",
  "is_new": false
}
GET /v1/auth/api-keys List API keys

Lists all active API keys for the authenticated organization.

Auth Required

Response 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"
  }
]
POST /v1/auth/api-keys Create a new API key

Generates a new API key for the authenticated organization. The full key is returned only once.

Auth Required

Request Body

ParameterTypeDescription
namestringOptional A label for this key
Response 201 Created
{
  "id": "01HKEY...",
  "api_key": "ac_live_sk_...",
  "name": "Production",
  "message": "Store this key — it won't be shown again"
}
DELETE /v1/auth/api-keys/{key_id} Revoke an API key

Soft-deletes an API key by setting it inactive. Returns 204 No Content on success.

Auth Required

Example Request
curl -X DELETE https://api.agenticcalling.ai/v1/auth/api-keys/01HKEY... \
  -H "Authorization: Bearer ac_live_sk_..."
PATCH /v1/auth/settings Update organization settings

Update organization settings such as BYOK (Bring Your Own Key) OpenAI API key.

Auth Required

Request Body

ParameterTypeDescription
openai_api_keystringOptional Your OpenAI API key (empty string clears it)
Response 200 OK
{ "updated": true }
POST /v1/auth/billing-portal Open Stripe billing portal

Creates a Stripe Billing Portal session and returns the URL. Redirect the user to manage their subscription.

Auth Required

Response 200 OK
{ "url": "https://billing.stripe.com/session/..." }
POST /v1/auth/checkout Create checkout session

Creates a Stripe Checkout session for a subscription upgrade.

Auth Required

Request Body

ParameterTypeDescription
price_idstringRequired Stripe price ID
success_urlstringRequired Redirect URL on success
cancel_urlstringRequired Redirect URL on cancel
Response 200 OK
{ "url": "https://checkout.stripe.com/..." }

Usage & Billing

Monitor your usage, billing, and plan limits.

GET /v1/usage Get current usage

Returns current usage, plan limits, remaining balance, and cost breakdown for the authenticated organization.

Auth Required

Example Request
curl https://api.agenticcalling.ai/v1/usage \
  -H "Authorization: Bearer ac_live_sk_..."
Response 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
  }
}
Check usage before large batch calls A batch with 50 calls and only 10 minutes remaining will be partially cancelled. Call GET /v1/usage first and warn the user.

What Counts as Usage

ResourceBilling Rule
Call minutesPer-second from when the call connects (human or voicemail answers). Ringing is free. No-answer/busy = free.
SMSPer message segment, outbound and inbound counted separately
Recording storageCumulative across all recordings, auto-purge per retention policy

Balance Behavior

ScenarioBehavior
Before a callAPI returns 402 Payment Required with upgrade URL
During active callNever cut off. Call completes gracefully. Overage absorbed.
During batchActive calls complete. Remaining queued calls cancelled.
Scheduled retriesPaused (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 IDUse CaseStrategy
platform.reservationBook tables, appointments, roomsEfficient, transactional
platform.price_inquiryGet quotes, rates, availabilityExploratory, asks about pricing/MOQ/terms
platform.negotiationHaggle, counter-offer, anchorStrategic, anchors low, counter-offers
platform.information_gatherSurveys, interviews, researchOpen-ended, neutral
platform.schedulingMove/cancel/confirm appointmentsDirect, confirms new details
platform.cold_outreachSales calls, lead generationPersuasive, qualifies and pitches
platform.complaintEscalate issues, get resolutionsAssertive, requests resolution
platform.follow_upCheck status, nudge responsesPolite, references previous interaction
platform.generalFallback for anything elseAdaptive, 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

  1. Call completes and transcript is finalized
  2. Platform runs extraction using the playbook's schema (or auto-generates one from the objective)
  3. Structured data is attached to the call response in structured_data
  4. A natural-language summary is 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."
Auto-extraction If no 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

EventWhen
call.queuedCall created and queued for dialing
call.attemptingPlatform dials (including retries)
call.connectedHuman answered
call.completedFinished successfully, includes structured_data and summary
call.attempt_failedSingle attempt failed (no_answer, busy, etc.)
call.needs_follow_upCallback requested by recipient
call.exhaustedAll retries failed (terminal)
call.callback_receivedSomeone called back your PhoneLine
call.channel_switchedRecipient asked to switch to SMS/email
call.dnc_requestedRecipient requested do-not-call
call.failedUnrecoverable error (terminal)

SMS Events

EventWhen
sms.sentOutbound SMS delivered
sms.receivedInbound SMS received on a PhoneLine
sms.schedule_extractedInbound SMS contained scheduling info
sms.dnc_detectedInbound SMS contained opt-out language

Billing Events

EventWhen
balance.warningUsage 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

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 CaseWebSocketsWebhooks
Live dashboard / transcript displayYesNo
Backend processing of completed callsNoYes
Agent integration (Claude, OpenClaw)NoYes
No public URL availableYesNo

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:

Claude Desktop / Claude Code / Cursor
{
  "mcpServers": {
    "AgenticCalling": {
      "command": "npx",
      "args": ["-y", "agenticcalling-mcp"],
      "env": {
        "AC_API_KEY": "YOUR_API_KEY"
      }
    }
  }
}
Selective tools
{
  "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

ToolDescription
make_callMake a phone call with objective, context, voice, language, behavior_rules
make_batch_callsCall multiple numbers concurrently (agent swarm)
get_call_statusCheck status, get transcript and structured data
list_callsList recent calls with statuses and results
create_playbookCreate/save a reusable call strategy
list_playbooksList available playbooks
send_smsSend a text message from agent's phone number
create_phone_lineProvision a new phone number
list_phone_linesList all provisioned numbers
get_usageCheck current usage and remaining balance

Claude.ai (Web / Mobile)

  1. Go to Claude Settings → Integrations → Add MCP
  2. Enter MCP server URL: https://mcp.agenticcalling.ai/sse
  3. Enter your API key when prompted
  4. Done. Claude now has calling tools.

Skills Installation

Claude Code
npx skills add autoacquire/agenticcalling-skills --skill agenticcalling --agent claude-code
Cursor
npx skills add autoacquire/agenticcalling-skills --skill agenticcalling --agent cursor
OpenClaw via ClawHub
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:

FormatURL
Documentation indexhttps://docs.agenticcalling.ai/llms.txt
Complete docs (one file)https://docs.agenticcalling.ai/llms-full.txt
Per-page markdownhttps://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

Coming Soon The Node.js SDK is under development. Use the REST API in the meantime.
npm install @agenticcalling/sdk  # Coming soon

Pricing & Rate Limits

Usage-based pricing with a generous free tier.

Plans

FreeProBusinessEnterprise
Monthly base$0$29$99Custom
US call minutes30/mo included$0.10/min$0.08/minVolume discount
International$0.15-0.40/min$0.12-0.35/minVolume discount
Phone lines3UnlimitedUnlimitedUnlimited
SMS50/mo$0.01/outbound$0.008/outboundVolume
Concurrent calls32050Custom
Recording storage1 GB (30 days)10 GB (90 days)50 GB (1 year)Custom
PlaybooksPlatform onlyCustom + platformCustom + platformCustom + marketplace
PodsYesYes
SupportCommunityEmailPriorityDedicated

Rate Limits

EndpointFreeProBusiness
POST /v1/calls10/min60/min200/min
POST /v1/calls/batch2/min10/min30/min
GET endpoints60/min300/min1000/min
POST /v1/auth/otp/send1 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

CodeNameDescription
200OKRequest succeeded
201CreatedResource created successfully
204No ContentSuccessful deletion
302FoundRedirect to signed URL (transcripts, recordings)
401UnauthorizedMissing or invalid API key
402Payment RequiredInsufficient balance. Includes upgrade URL and usage details.
404Not FoundResource not found (call, key, transcript not ready)
409ConflictCannot cancel a call in its current state
422Unprocessable EntityValidation error or DNC blocked number
429Too Many RequestsRate limited. Check Retry-After header.
502Bad GatewayFailed to generate signed URL for recording/transcript
503Service UnavailableExternal service not configured (e.g., Stripe)

SDK Exception Classes

ExceptionHTTP CodeProperties
AuthenticationError401Invalid API key
InsufficientBalanceError402.minutes_used, .minutes_limit, .resets_at, .upgrade_url
NotFoundError404Resource not found
ValidationError422Bad parameters
RateLimitError429.retry_after (seconds)
AgenticCallingError5xxServer 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.")