KaiCalls API
Give your AI agent a phone. Make outbound calls, manage agents, and integrate AI phone assistants into your applications.
https://kaicalls.com/api/v1Quick Start
1. Get your API key
Go to Dashboard → Settings → API Keys and generate a key.
2. Make your first call
curl -X POST https://kaicalls.com/api/v1/calls \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "your-agent-id",
"to": "+15125551234",
"name": "John Smith",
"context": "Confirm appointment for tomorrow at 2pm"
}'3. Check the result
curl "https://kaicalls.com/api/v1/calls?id=CALL_ID" \
-H "Authorization: Bearer kc_live_xxxxx"Authentication
All API requests require a Bearer token in the Authorization header.
Authorization: Bearer kc_live_xxxxxKey Prefixes
kc_live_— Production keyskc_test_— Sandbox keys (coming soon)
Scopes
Keys are created with scopes that control access. Default scopes are granted automatically.
| Parameter | Type | Description |
|---|---|---|
agents:read | scope | List and view agents |
agents:write | scope | Create and update agents |
numbers:read | scope | List phone numbers |
calls:read | scope | List and view call records |
calls:write | scope | Make outbound calls |
* | scope | Full access (all scopes) |
Agents
/api/v1/agentsList all agentsReturns all agents assigned to your businesses. Requires agents:read scope.
{
"agents": [
{
"id": "uuid-abc123",
"name": "Kai",
"business_id": "uuid-biz456",
"created_at": "2026-01-15T10:00:00Z"
}
]
}/api/v1/agents?id=AGENT_IDGet agent detailsReturns detailed info including Vapi assistant ID and assigned phone number.
{
"id": "uuid-abc123",
"name": "Kai",
"business_id": "uuid-biz456",
"vapi_assistant_id": "vapi-asst-789",
"phone_number": "+15125551234",
"created_at": "2026-01-15T10:00:00Z"
}/api/v1/agentsCreate a new voice agentCreates a Vapi assistant with lead collection capabilities and registers the agent in the KaiCalls database. If database insertion fails, the Vapi assistant is automatically cleaned up.
Required scope: agents:write
Parameters
| Parameter | Type | Description |
|---|---|---|
business_idrequired | string | Business to assign the agent to (must be accessible by your API key) |
namerequired | string | Agent display name |
system_promptrequired | string | The agent's system prompt / instructions |
first_message | string | Greeting message (default: "Hello! How can I help you today?") |
voice | object | Voice configuration: { provider, voiceId } (defaults to Vapi default) |
model | object | Model configuration: { provider, model, temperature } (defaults to OpenAI gpt-4.1 at 0.7) |
metadata | object | Extra metadata to store with the agent (e.g. description, custom fields) |
Example
curl -X POST https://kaicalls.com/api/v1/agents \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"business_id": "uuid-biz456",
"name": "Kai Receptionist",
"system_prompt": "You are a friendly receptionist for Smith Law. Answer questions about services and collect caller information.",
"first_message": "Hello! Thanks for calling Smith Law. How can I help you today?",
"voice": { "provider": "elevenlabs", "voiceId": "voice-id-123" },
"model": { "provider": "openai", "model": "gpt-4.1", "temperature": 0.7 }
}'{
"id": "uuid-agent789",
"name": "Kai Receptionist",
"business_id": "uuid-biz456",
"vapi_assistant_id": "vapi-asst-xyz",
"created_at": "2026-03-06T10:00:00Z"
}Automatic Lead Collection
Every agent created via the API automatically includes lead collection capabilities. The agent will extract caller name, email, phone, and intent during conversations without any additional configuration.
/api/v1/agentsUpdate an existing agentPartial updates to agent configuration. Only include the fields you want to change. Changes are synced to both the Vapi assistant and the KaiCalls database. Metadata updates are merged with existing values (not replaced).
Required scope: agents:write
Parameters
| Parameter | Type | Description |
|---|---|---|
idrequired | string | KaiCalls agent ID to update |
name | string | New agent display name |
system_prompt | string | New system prompt / instructions |
first_message | string | New greeting message |
voice | object | New voice configuration: { provider, voiceId } |
model | object | New model configuration: { provider, model, temperature } |
metadata | object | Metadata to merge with existing values |
Example (update system prompt only)
curl -X PATCH https://kaicalls.com/api/v1/agents \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"id": "uuid-agent789",
"system_prompt": "You are an intake specialist. Collect case details and schedule consultations."
}'{
"id": "uuid-agent789",
"name": "Kai Receptionist",
"business_id": "uuid-biz456",
"vapi_assistant_id": "vapi-asst-xyz",
"updated": true
}Phone Numbers
/api/v1/numbersList phone numbersReturns all phone numbers registered to your businesses. Requires numbers:read scope.
{
"numbers": [
{
"id": "uuid-num789",
"phone_number": "+15125551234",
"agent_id": "uuid-abc123",
"business_id": "uuid-biz456",
"created_at": "2026-01-15T10:00:00Z"
}
]
}Calls
/api/v1/callsMake an outbound callInitiates an outbound call using your AI agent. The system automatically enriches the call with context from your CRM — the agent will know the caller's name, lead score, interaction history, and business details.
Required scope: calls:write
Parameters
| Parameter | Type | Description |
|---|---|---|
agent_idrequired | string | The agent to use for this call |
torequired | string | Phone number to call (E.164 format, e.g. +15125551234) |
name | string | Customer name. The agent greets them by name. Also accepts customer_name. |
context | string | Freeform context the agent can reference. Use for task instructions, appointment details, or notes. |
firstMessage | string | Override the agent's opening line for this call. Also accepts first_message. If omitted, uses the agent's configured outbound greeting. |
lead_id | string | Link this call to a lead in your CRM. Enables automatic enrichment with lead score, history, and source. |
webhook_url | string | URL to receive a webhook when the call completes |
max_duration | integer | Maximum call duration in seconds (default: 600) |
Automatic Context Enrichment
When you provide a lead_id or the to number matches an existing lead, the agent automatically receives these variables in its prompt:
| Variable | Description |
|---|---|
name | Lead's first name (from name param or CRM) |
lead_score | AI-assigned quality score (0-100) |
lead_status | Current lead status in your pipeline |
interaction_count | Number of previous calls with this lead |
days_since_first_contact | Days since the lead was first created |
lead_source | Where the lead came from (web form, referral, etc.) |
business_name | Your business name |
business_phone | Your office phone number |
time_of_day | "morning", "afternoon", or "evening" |
These are injected automatically — you don't need to pass them.
Minimal Example
curl -X POST https://kaicalls.com/api/v1/calls \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{"agent_id": "uuid-abc123", "to": "+15125559876"}'Full Example (with context and lead)
curl -X POST https://kaicalls.com/api/v1/calls \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "uuid-abc123",
"to": "+15125559876",
"name": "John Smith",
"lead_id": "uuid-lead123",
"context": "Following up on car accident case",
"firstMessage": "Hey John, this is Kai from Smith Law — do you have a minute?"
}'{
"id": "uuid-call789",
"conversation_id": "vapi-conv-xyz",
"status": "queued",
"agent_id": "uuid-abc123",
"to": "+15125559876",
"name": "John Smith",
"created_at": "2026-02-15T14:30:00Z"
}/api/v1/callsList callsReturns calls across all your businesses, ordered by most recent. Requires calls:read scope.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Max results per page (default: 50, max: 100) |
agent_id | string | Filter by agent ID |
status | string | Filter by call status |
after | string | Cursor-based pagination. Pass the created_at of the last result to get the next page. |
{
"calls": [
{
"id": "uuid-call789",
"conversation_id": "vapi-conv-xyz",
"status": "completed",
"duration": 127,
"agent_id": "uuid-abc123",
"agent_name": "Kai",
"created_at": "2026-02-15T14:30:00Z"
}
],
"has_more": true
}/api/v1/calls?id=CALL_IDGet call detailsReturns full details for a single call including summary, recording, and quality analysis.
{
"id": "uuid-call789",
"conversation_id": "vapi-conv-xyz",
"status": "completed",
"direction": "inbound",
"duration": 127,
"agent_id": "uuid-abc123",
"agent_name": "Kai",
"business_id": "uuid-biz456",
"lead_id": "uuid-lead123",
"summary": "Confirmed appointment with John for tomorrow at 2pm.",
"recording_url": "https://storage.example.com/recordings/call_xyz.mp3",
"quality_dimensions": {
"professionalism": 92,
"empathy": 88,
"information_gathering": 95,
"resolution": 90
},
"created_at": "2026-02-15T14:30:00Z"
}API Keys
Key management uses session authentication (dashboard login), not API key auth.
/api/v1/keysList your API keys{
"keys": [
{
"id": "uuid-key123",
"prefix": "kc_live_abc1",
"name": "Production Key",
"scopes": ["agents:read", "calls:read", "calls:write", "numbers:read"],
"business_id": null,
"created_at": "2026-01-15T10:00:00Z",
"last_used_at": "2026-02-15T14:30:00Z",
"expires_at": null,
"is_active": true
}
]
}/api/v1/keysCreate a new keyThe full key is returned only once — store it securely.
| Parameter | Type | Description |
|---|---|---|
name | string | Human-readable name (default: "Untitled Key") |
business_id | string | Scope key to a single business. If omitted, key accesses all your businesses. |
scopes | string[] | Permission scopes. Defaults to ["agents:read", "numbers:read", "calls:read", "calls:write"]. |
{
"key": "kc_live_abc123def456...",
"id": "uuid-key123",
"prefix": "kc_live_abc1",
"name": "My Integration Key",
"scopes": ["agents:read", "calls:read", "calls:write", "numbers:read"],
"business_id": "uuid-biz456",
"created_at": "2026-02-15T14:30:00Z"
}/api/v1/keys?id=KEY_IDRevoke a keyRevokes an API key. The key immediately stops working.
{ "success": true }Account
/api/v1/balanceGet subscription infoReturns usage and subscription status for each of your businesses.
{
"businesses": [
{
"business_id": "uuid-biz456",
"minutes_used": 412,
"phone_numbers": 2,
"subscription_status": "active"
}
]
}/api/v1/usageGet API usage logReturns your API call history.
| Parameter | Type | Description |
|---|---|---|
start | string | ISO 8601 start date filter |
end | string | ISO 8601 end date filter |
limit | integer | Max results (default: 100, max: 500) |
{
"usage": [
{
"id": "uuid-log123",
"endpoint": "/v1/calls",
"method": "POST",
"status_code": 201,
"call_id": "uuid-call789",
"cost_cents": 15,
"created_at": "2026-02-15T14:30:00Z"
}
],
"has_more": false
}Webhooks
Pass a webhook_url when making a call to receive events when the call completes or fails.
call.completed
{
"event": "call.completed",
"call_id": "uuid-call789",
"status": "completed",
"duration": 127,
"recording_url": "https://...",
"summary": "Confirmed appointment for tomorrow at 2pm.",
"timestamp": "2026-02-15T14:32:12Z"
}call.failed
{
"event": "call.failed",
"call_id": "uuid-call789",
"status": "failed",
"error": "no_answer",
"timestamp": "2026-02-15T14:31:00Z"
}Errors
All errors follow a consistent format:
{
"error": {
"code": "error_code",
"message": "Human-readable description"
}
}| Code | Status | Description |
|---|---|---|
unauthorized | 401 | Invalid, missing, expired, or revoked API key |
forbidden | 403 | Key doesn't have the required scope |
not_found | 404 | Agent, call, or resource not found |
invalid_request | 400 | Missing required fields or invalid JSON |
vapi_error | 4xx/5xx | Vapi API call failed (create/update agent) |
call_failed | 500 | Vapi failed to initiate the call |
internal_error | 500 | Unexpected server error |
rate_limited | 429 | Too many requests — slow down |
Rate Limits
| Endpoint | Limit |
|---|---|
| POST /v1/calls | 60 requests/minute |
| POST /v1/agents | 30 requests/minute |
| PATCH /v1/agents | 60 requests/minute |
| All GET endpoints | 300 requests/minute |
SDKs
Python
pip install kaicallsfrom kaicalls import KaiCalls
kai = KaiCalls(api_key="kc_live_xxx")
# Simple call — agent auto-enriches with CRM data
call = kai.calls.create(
agent_id="uuid-abc123",
to="+15125551234",
name="John Smith"
)
# Full call with lead linking and custom opening
call = kai.calls.create(
agent_id="uuid-abc123",
to="+15125551234",
name="John Smith",
lead_id="uuid-lead123",
context="Following up on car accident case",
first_message="Hey John, this is Kai — do you have a minute?"
)
result = kai.calls.wait(call.id)
print(result.summary)
# Create an agent
agent = kai.agents.create(
business_id="uuid-biz456",
name="Kai Receptionist",
system_prompt="You are a friendly receptionist for Smith Law.",
first_message="Hello! How can I help you today?"
)
# Update an agent
kai.agents.update(id=agent.id, system_prompt="New instructions.")JavaScript / TypeScript
npm install kaicallsimport { KaiCalls } from 'kaicalls';
const kai = new KaiCalls({ apiKey: 'kc_live_xxx' });
// Simple call
const call = await kai.calls.create({
agentId: 'uuid-abc123',
to: '+15125551234',
name: 'John Smith',
});
// Full call with lead linking and custom opening
const call = await kai.calls.create({
agentId: 'uuid-abc123',
to: '+15125551234',
name: 'John Smith',
leadId: 'uuid-lead123',
context: 'Following up on car accident case',
firstMessage: 'Hey John, this is Kai — do you have a minute?',
});
const result = await kai.calls.get(call.id);
console.log(result.summary);
// Create an agent
const agent = await kai.agents.create({
businessId: 'uuid-biz456',
name: 'Kai Receptionist',
systemPrompt: 'You are a friendly receptionist for Smith Law.',
firstMessage: 'Hello! How can I help you today?',
});
// Update an agent
await kai.agents.update({
id: agent.id,
systemPrompt: 'New instructions.',
});Need help? support@kaicalls.com