KaiCalls API
Give your AI agent a phone. Make outbound calls, manage agents, and integrate AI phone assistants into your applications.
https://www.kaicalls.com/api/v1Send Your AI Agent to KaiCalls📞
New accounts can sign up, get a phone number, and start taking calls through the API.
Send this to your agent
Copy the instruction below
Sign up via the API
Gets API key + phone number
Agent starts making calls
30-day free trial, no card
Click to copy — for existing accounts, use the Claude connector section below
Quick 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://www.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://www.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 keys (only prefix issued today; all calls hit live infrastructure and are billed)
A separate kc_test_ sandbox prefix is on the roadmap but not yet available — do not assume one exists when integrating.
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 |
leads:read | scope | List and view leads |
leads:write | scope | Create and update leads |
sms:read | scope | List and view SMS messages |
sms:write | scope | Send SMS messages |
emails:read | scope | List and view email queue |
emails:write | scope | Send and auto-approve emails |
webhooks:read | scope | View webhook configuration |
webhooks:write | scope | Configure or remove webhooks |
evals:read | scope | List eval scenarios and read run results |
evals:write | scope | Create, update, delete, and run eval scenarios |
* | scope | Full access (all scopes) |
Claude Connector / MCP
Claude users can connect their Kai Calls account through the remote MCP connector. Once approved, Claude can list agents, review recent calls, read transcripts, and start outbound calls through accessible Kai Calls agents.
Connector URL
https://www.kaicalls.com/api/mcpConnect with OAuth
- Add a custom connector in Claude using the connector URL above.
- Claude discovers the Kai Calls OAuth metadata automatically.
- Sign in with your Kai Calls account and approve access.
- Claude receives a short-lived bearer token scoped to your approved access.
/.well-known/oauth-protected-resourceMCP protected-resource metadata/.well-known/oauth-authorization-serverOAuth authorization server metadata/.well-known/openid-configurationOpenID-compatible OAuth metadatahttps://www.kaicalls.com/.well-known/oauth-protected-resource
https://www.kaicalls.com/.well-known/oauth-authorization-server
https://www.kaicalls.com/.well-known/openid-configurationPermissions and tools
Access is scoped to the authenticated Kai Calls user and the businesses linked to that account. Claude cannot access API keys, dashboard settings, private environment values, or businesses outside the approved account.
| Parameter | Type | Description |
|---|---|---|
list_agents | agents:read | List Kai Calls agents available to the authenticated account |
get_business_info | agents:read | Show business details, agent count, and recent call volume |
list_recent_calls | calls:read | List recent calls, optionally filtered by agent or status |
check_call_status | calls:read | Check one call by Kai Calls call ID |
get_transcript | calls:read | Retrieve a completed call transcript and summary |
make_call | calls:write | Start an outbound call with agent_id, to, and optional context or lead_id |
Direct MCP usage
API-key auth still works for developers and non-Claude MCP clients. Pass a Kai Calls API key as a bearer token to the JSON-RPC endpoint or the REST-style helper endpoints.
/api/mcpJSON-RPC MCP endpointcurl -X POST https://www.kaicalls.com/api/mcp \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'/api/mcp/initializeMCP handshake/api/mcp/tools/listList MCP tools/api/mcp/tools/callCall one MCP toolcurl -X POST https://www.kaicalls.com/api/mcp/tools/call \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"name": "list_recent_calls",
"arguments": { "limit": 5 }
}'Signup
Create a full KaiCalls account via the API — no UI session required. This endpoint provisions a business, generates an API key, creates an AI agent, and assigns a phone number in a single call.
/api/v1/signupCreate a new accountNo authentication required. Provisions a complete account: auth user, business, API key, AI intake agent, and phone number from the available pool. Returns everything needed to start making API calls immediately.
Rate limit: 5 requests/hour per IP
Parameters
| Parameter | Type | Description |
|---|---|---|
business_namerequired | string | Name of the business |
emailrequired | string | Account email address (must be unique) |
business_type | string | Category of business (e.g. "Legal Services", "Healthcare") |
website | string | Business website URL |
phone_forward_to | string | Phone number to forward calls to (sets up ring-first routing — AI answers if no pickup after 20s) |
Example
curl -X POST https://www.kaicalls.com/api/v1/signup \
-H "Content-Type: application/json" \
-d '{
"business_name": "Smith Law Firm",
"email": "contact@smithlaw.com",
"business_type": "Legal Services",
"website": "https://smithlaw.com",
"phone_forward_to": "+15125551234"
}'Response
{
"api_key": "kc_live_abc123def456...",
"business_id": "uuid-biz456",
"agent_id": "uuid-agent789",
"phone_number": "+15125559876",
"dashboard_url": "https://www.kaicalls.com/dashboard",
"trial_ends_at": "2026-04-14T00:00:00.000Z"
}Error Responses
| Code | Status | Description |
|---|---|---|
validation_error | 400 | Missing or invalid business_name or email |
email_exists | 409 | An account with this email already exists |
rate_limited | 429 | Too many signups from this IP (5/hour limit) |
payment_required | 402 | x402 payment required (when enabled) |
What Gets Created
- Auth user with auto-confirmed email
- Business with 30-day free trial
- API key with full default scopes
- AI intake agent (ready to take calls)
- Phone number from pool (with Vapi + Twilio)
- Call forwarding (if
phone_forward_toprovided)
Agents
Editing agents from Claude Code or another agent CLI?
Drop in our official kaicalls-agent-api skill — it teaches your agent the full /api/v1/agents contract, the snake_case body shape, the vapi_config pass-through, and the common mistakes to avoid. Works with Claude Code, Codex, and any agent runtime that supports skill bundles.
/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 prompts (inbound/outbound/SMS), voice, model, Vapi assistant ID, and phone number. The inbound prompt is fetched live from Vapi.
?id= query param, not a path segment. GET /api/v1/agents/<id> returns a 404 — there is no dynamic route. Always pass the agent UUID as ?id=.{
"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",
"prompts": {
"inbound": {
"prompt": "You are a friendly receptionist for Smith Law...",
"llm": "gpt-4.1"
},
"outbound": {
"prompt": "You are calling {{name}} to follow up...",
"llm": "gpt-4.1",
"first_message": "Hey {{name}}, this is Kai from Smith Law."
},
"sms": {
"prompt": "Respond helpfully to text messages...",
"llm": "gpt-4.1"
}
},
"voice": { "provider": "elevenlabs", "voiceId": "voice-id-123" },
"model": { "provider": "openai", "model": "gpt-4.1", "temperature": 0.7 },
"first_message": "Hello! How can I help you today?"
}/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://www.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).
Agents have separate prompts per channel: inbound (incoming calls, synced to Vapi), outbound (AI-initiated calls, applied at call time), and SMS (text responses).
Required scope: agents:write
Parameters
| Parameter | Type | Description |
|---|---|---|
idrequired | string | KaiCalls agent ID to update |
name | string | New agent display name |
inbound_prompt | string | System prompt for incoming calls (synced to Vapi + metadata) |
outbound_prompt | string | System prompt for outbound calls (metadata only, applied at call time) |
sms_prompt | string | System prompt for SMS responses (metadata only) |
system_prompt | string | Legacy alias for inbound_prompt (still supported) |
first_message | string | New greeting message for inbound calls |
outbound_first_message | string | Custom opening message for outbound calls |
outbound_llm | string | Override model for outbound calls (e.g. gpt-4.1) |
sms_llm | string | Override model for SMS (e.g. gpt-4.1) |
voice | object | New voice configuration: { provider, voiceId } |
model | object | New model configuration: { provider, model, temperature } |
metadata | object | Metadata to merge with existing values |
vapi_config | object | Direct Vapi assistant config passthrough. Any valid Vapi field is accepted: stopSpeakingPlan, startSpeakingPlan, endCallPhrases, endCallMessage, transcriber, analysisPlan, artifactPlan, backgroundSpeechDenoisingPlan, monitorPlan, silenceTimeoutSeconds, maxDurationSeconds, etc. Sanitized and forwarded to Vapi. |
Example (update inbound prompt)
curl -X PATCH https://www.kaicalls.com/api/v1/agents \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"id": "uuid-agent789",
"inbound_prompt": "You are an intake specialist. Collect case details and schedule consultations."
}'Example (update outbound prompt)
curl -X PATCH https://www.kaicalls.com/api/v1/agents \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"id": "uuid-agent789",
"outbound_prompt": "You are calling {{name}} about their inquiry. Qualify and book a consultation.",
"outbound_first_message": "Hey {{name}}, this is Kai from Smith Law — do you have a minute?"
}'Example (Vapi config — interruption handling)
curl -X PATCH https://www.kaicalls.com/api/v1/agents \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"id": "uuid-agent789",
"vapi_config": {
"stopSpeakingPlan": {
"numWords": 2,
"voiceSeconds": 0.3,
"backoffSeconds": 1,
"interruptionPhrases": ["stop", "wait", "hold on", "shut up", "enough", "no", "actually"],
"acknowledgementPhrases": ["okay", "yeah", "right", "uh-huh", "mm-hmm", "got it"]
},
"endCallPhrases": ["goodbye", "talk to you later", "have a nice day"],
"endCallMessage": "Thanks for calling! Goodbye."
}
}'{
"id": "uuid-agent789",
"name": "Kai Receptionist",
"business_id": "uuid-biz456",
"vapi_assistant_id": "vapi-asst-xyz",
"updated": true,
"updated_fields": ["inbound_prompt"]
}Common mistakes
- Path segment for GET single agent. Use
?id=<agent_id>, not/agents/<id>. - camelCase fields. The body is
snake_case:inbound_prompt,first_message,vapi_config. camelCase keys are silently ignored. - Wrong agent ID. Pass the KaiCalls agent UUID (from
GET /api/v1/agents), not the Vapi assistant ID. The endpoint resolves Vapi for you. - Round-tripping the full prompt unchanged. Every PATCH that touches the prompt writes an audit-history row and queues an owner-notification email. Send only the fields you actually changed.
- Patching Vapi directly. Updating the Vapi assistant outside this endpoint leaves the KaiCalls DB stale (dashboard shows wrong data, no audit trail, no owner notification, lead-collection evaluation plans get wiped). Always go through
/api/v1/agents.
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://www.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://www.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"
}Leads
/api/v1/leadsList leadsReturns leads across all your businesses, ordered by most recent. Requires leads:read scope.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: new, contacted, qualified, converted, lost |
source | string | Filter by lead source |
agent_id | string | Filter by agent ID |
phone | string | Filter by exact phone number |
email | string | Filter by exact email address |
limit | integer | Max results per page (default: 50, max: 100) |
after | string | Cursor-based pagination. Pass the created_at of the last result to get the next page. |
Example
curl "https://www.kaicalls.com/api/v1/leads?status=new&limit=10" \
-H "Authorization: Bearer kc_live_xxxxx"{
"leads": [
{
"id": "uuid-lead123",
"name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"phone": "+15551234567",
"status": "new",
"source": "inbound_call",
"agent_id": "uuid-abc123",
"agent_name": "Reception AI",
"business_id": "uuid-biz456",
"created_at": "2026-03-17T12:00:00Z",
"updated_at": "2026-03-17T12:00:00Z"
}
],
"has_more": false
}/api/v1/leads?id=LEAD_IDGet lead detailsReturns full details for a single lead including address, notes, and event info.
{
"id": "uuid-lead123",
"name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"phone": "+15551234567",
"zip": "90210",
"address": "123 Main St",
"city": "Beverly Hills",
"state": "CA",
"status": "qualified",
"source": "inbound_call",
"source_url": null,
"source_page": null,
"agent_id": "uuid-abc123",
"agent_name": "Reception AI",
"business_id": "uuid-biz456",
"notes": "Interested in premium plan",
"message": null,
"event_date": null,
"event_type": null,
"created_at": "2026-03-17T12:00:00Z",
"updated_at": "2026-03-17T14:30:00Z"
}/api/v1/leadsUpdate a leadUpdate a lead's information. Only provided fields are modified. Requires leads:write scope.
Body Parameters
| Parameter | Type | Description |
|---|---|---|
idrequired | string | Lead ID to update |
name | string | Full name |
first_name | string | First name |
last_name | string | Last name |
email | string | Email address |
phone | string | Phone number |
status | string | new, contacted, qualified, converted, or lost |
source | string | Lead source |
notes | string | Internal notes |
address | string | Street address |
city | string | City |
state | string | State |
zip | string | ZIP code |
Example
curl -X PATCH https://www.kaicalls.com/api/v1/leads \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"id": "uuid-lead123",
"status": "qualified",
"notes": "Ready for follow-up call"
}'{
"id": "uuid-lead123",
"name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"phone": "+15551234567",
"zip": "90210",
"address": "123 Main St",
"city": "Beverly Hills",
"state": "CA",
"status": "qualified",
"source": "inbound_call",
"agent_id": "uuid-abc123",
"agent_name": "Reception AI",
"business_id": "uuid-biz456",
"notes": "Ready for follow-up call",
"created_at": "2026-03-17T12:00:00Z",
"updated_at": "2026-03-17T15:45:00Z"
}SDR Pipeline
Automated sales development pipeline. Segments leads, decides the next-best action (call/email/SMS/skip/escalate), enforces cadence, and records every touch in sdr_activity_log. All SDR routes accept a Bearer API key or a browser session. API keys need sdr:read for GET routes and sdr:write for POST / PUT routes; browser sessions bypass the scope check. The caller is always bound to the businessId they pass.
Segments: speed_to_lead, fresh_demo_caller, warm_demo_caller, cooling_demo, new_trial_stuck, trial_going_dark, trial_ending, contacted_no_reply, exhausted, churned_recent, cold.
/api/sdr/pipelineList leads in the SDR pipelineQuery Parameters
| Parameter | Type | Description |
|---|---|---|
businessIdrequired | string | Business to review |
segment | string | Filter to a single segment |
limit | integer | Max results (default 100) |
Example
curl "https://www.kaicalls.com/api/sdr/pipeline?businessId=uuid-biz&segment=speed_to_lead" \
-H "Authorization: Bearer kc_live_xxxxx"{
"businessId": "uuid-biz",
"enabled": true,
"summary": { "total_leads": 42, "actionable": 30, "needs_manual": 2 },
"leads": [
{
"id": "uuid-lead",
"name": "Jane Doe",
"email": "jane@example.com",
"phone": "+15551234567",
"status": "new",
"source": "inbound_call",
"segment": "speed_to_lead",
"score": 78,
"touch_count": 0,
"last_touch": null,
"next_action": "call",
"paused": false,
"created_at": "2026-04-14T12:00:00Z",
"updated_at": "2026-04-14T12:00:00Z"
}
]
}/api/sdr/actionTrigger an action on a single leadEnforces cadence (min_touch_gap_hours, max_touches). Returns 429 with nextAllowedAt if rate-limited.
Body Parameters
| Parameter | Type | Description |
|---|---|---|
businessIdrequired | string | Business the lead belongs to |
leadIdrequired | string | Lead to act on |
actionrequired | string | One of: call, email, sms, skip, pause, resume |
Example
curl -X POST https://www.kaicalls.com/api/sdr/action \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"businessId": "uuid-biz",
"leadId": "uuid-lead",
"action": "call"
}'{
"success": true,
"action": "call",
"leadId": "uuid-lead",
"detail": "Call queued"
}/api/sdr/configRead SDR configurationReturns the current config; missing rows fall back to safe defaults.
curl "https://www.kaicalls.com/api/sdr/config?businessId=uuid-biz" \
-H "Authorization: Bearer kc_live_xxxxx"/api/sdr/configUpsert SDR configurationBody Parameters
| Parameter | Type | Description |
|---|---|---|
businessIdrequired | string | Business to configure |
enabled | boolean | Master toggle for SDR automation |
min_touch_gap_hours | integer | Minimum hours between touches on the same lead |
max_touches | integer | Hard cap; leads past this become exhausted |
auto_call | boolean | Allow dispatcher to place outbound calls |
auto_email | boolean | Allow dispatcher to queue / auto-approve emails |
auto_sms | boolean | Allow dispatcher to send SMS |
sdr_agent_id | string | Vapi agent used for outbound SDR calls |
business_hours_start | integer | 0–23, caller-local time |
business_hours_end | integer | 0–23, caller-local time |
email_from_account_id | string | connected_email_accounts.id to send from |
/api/sdr/runManually trigger a pipeline runReviews the full pipeline and dispatches actions. Pass { "dryRun": true } to preview without dispatching. Returns 400 if SDR is not enabled on the business's config.
curl -X POST https://www.kaicalls.com/api/sdr/run \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{ "businessId": "uuid-biz", "dryRun": true }'{
"success": true,
"summary": {
"calls_queued": 3,
"emails_queued": 5,
"sms_sent": 2,
"escalated": 1,
"failed": 0
},
"results": [
{ "success": true, "action": "call", "leadId": "uuid-lead", "detail": "Call queued" }
],
"skipped": 0,
"errors": []
}/api/sdr/reportAggregated SDR pipeline reportQuery Parameters
| Parameter | Type | Description |
|---|---|---|
businessIdrequired | string | Business to report on |
period | string | today | week | month (default: week) |
Returns action counts, outcome counts, new leads, hot leads (speed_to_lead / fresh_demo_caller / new_trial_stuck), and stale leads (on manual_review).
/api/sdr/activityRecent SDR activity logQuery Parameters
| Parameter | Type | Description |
|---|---|---|
businessIdrequired | string | Business to list activity for |
leadId | string | Filter to a single lead |
limit | integer | Max results (default 50, max 200) |
Scheduled runner
/api/cron/sdr-pipeline runs every 2 hours (0 */2 * * *), iterates every business with sdr_config.enabled = true, and dispatches. Cron-secret protected; not callable by API key or session.
SMS
/api/v1/sms/sendSend an outbound SMSSends an SMS via Twilio using the agent's assigned phone number. Includes DNC and consent compliance checks before sending. Messages are logged to the conversation thread.
Required scope: sms:write
Parameters
| Parameter | Type | Description |
|---|---|---|
torequired | string | Recipient phone number (E.164 format, e.g. +15125551234) |
from_agent_idrequired | string | Agent UUID — the agent's Twilio number is used as the sender |
messagerequired | string | Text message content |
lead_id | string | Optional lead UUID to link the message to a conversation thread |
Example
curl -X POST https://www.kaicalls.com/api/v1/sms/send \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"to": "+15125559876",
"from_agent_id": "uuid-agent789",
"message": "Hi John, just following up on our call. Let me know if you have any questions!",
"lead_id": "uuid-lead123"
}'{
"success": true,
"message_sid": "SM1234567890abcdef",
"to": "+15125559876",
"from": "+15125551234"
}| Error Code | Status | Description |
|---|---|---|
number_on_dnc | 400 | Recipient is on the Do-Not-Call list |
number_opted_out | 400 | Recipient has not opted in to SMS |
not_found | 404 | Agent not found or has no phone number |
sms_send_failed | 500 | Twilio failed to deliver the message |
/api/v1/sms/update-promptUpdate an agent's SMS promptUpdates the SMS auto-reply prompt for an agent. Useful for AI agents that need to adjust their own follow-up messaging programmatically. Changes are logged to the audit trail.
Required scope: agents:write
Parameters
| Parameter | Type | Description |
|---|---|---|
agent_idrequired | string | Agent UUID to update |
sms_promptrequired | string | New SMS system prompt text |
reason | string | Optional audit reason for the change |
Example
curl -X POST https://www.kaicalls.com/api/v1/sms/update-prompt \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "uuid-agent789",
"sms_prompt": "You are a helpful legal assistant. Respond to text messages professionally and help schedule consultations.",
"reason": "Switching to consultation-focused messaging"
}'{
"success": true,
"agent_id": "uuid-agent789",
"message": "SMS prompt updated"
}Evals
Eval scenarios are scripted mock conversations that run against an agent and grade each assistant turn with an AI judge (default: GPT-4.1). Use them to gate regressions on prompt changes — every scenario tests one behavior (greeting language, intake step, refusal, recall of a fact). Destructive tools (send_sms, send_link, bookings, config writes) are auto-stubbed during runs, so evals never trigger real SMS, emails, or bookings.
Each scenario is a sequence of turns. User turns are { "role": "user", "content": "..." }. Assistant turns include a judgePlan with one of three judge types: exact (string match), regex (regex match), or ai (LLM rubric).
/api/v1/evalsCreate an eval scenarioRequires evals:write. Creates the scenario in KaiCalls and mirrors it to Vapi.
Body Parameters
| Parameter | Type | Description |
|---|---|---|
agent_idrequired | string | Agent UUID — scenario runs against this agent's Vapi assistant. |
business_idrequired | string | Business that owns the agent. Must be accessible to the API key. |
namerequired | string | Human-readable scenario name (e.g. greeting.identifies_firm). |
description | string | What the scenario tests. |
messagesrequired | VapiEvalMessage[] | Ordered turns. Alternate user / assistant; assistant turns carry a judgePlan. |
Example
curl -X POST https://www.kaicalls.com/api/v1/evals \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "uuid-agent789",
"business_id": "uuid-biz456",
"name": "greeting.identifies_firm",
"description": "Agent confirms the firm name when asked.",
"messages": [
{ "role": "user", "content": "Hi, is this the law office?" },
{
"role": "assistant",
"judgePlan": {
"type": "ai",
"model": {
"provider": "openai",
"model": "gpt-4.1",
"messages": [
{
"role": "system",
"content": "Evaluate: {{messages[-1]}}\nContext: {{messages}}\n\nPass if the assistant confirms the firm name AND offers to help. Fail if it names a different firm or refuses.\n\nOutput exactly: pass or fail"
}
]
}
}
}
]
}'{
"id": "uuid-eval-001",
"agent_id": "uuid-agent789",
"business_id": "uuid-biz456",
"vapi_eval_id": "vapi-eval-abc",
"name": "greeting.identifies_firm",
"description": "Agent confirms the firm name when asked.",
"messages": [/* ... */],
"created_at": "2026-05-01T10:00:00Z"
}/api/v1/evalsList evals (filter by agent_id) or fetch one (?id=)Requires evals:read. List omits messages for brevity; fetch by id to get the full scenario.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Fetch a single eval by ID (returns full messages). |
agent_id | string | Filter list to one agent. |
# List all evals for an agent
curl "https://www.kaicalls.com/api/v1/evals?agent_id=uuid-agent789" \
-H "Authorization: Bearer kc_live_xxxxx"
# Fetch one eval
curl "https://www.kaicalls.com/api/v1/evals?id=uuid-eval-001" \
-H "Authorization: Bearer kc_live_xxxxx"/api/v1/evalsUpdate an evalRequires evals:write. Send only the fields you want to change. Updates are mirrored to Vapi.
| Parameter | Type | Description |
|---|---|---|
idrequired | string | Eval ID. |
name | string | Rename the scenario. |
description | string | Replace the description. |
messages | VapiEvalMessage[] | Replace the full turn list. |
/api/v1/evals?id=EVAL_IDDelete an evalRequires evals:write. Deletes the local row and the Vapi mirror.
/api/v1/evals/runRun an eval (or every eval for an agent)Requires evals:write (runs bill Vapi compute). Pass eval_id to run one scenario, or agent_id to fan out every scenario for the agent in parallel.
Body Parameters
| Parameter | Type | Description |
|---|---|---|
eval_id | string | Run one scenario by ID. (Provide eval_id OR agent_id.) |
agent_id | string | Run every scenario for this agent in parallel. |
wait | boolean | true (default) blocks until the run ends; false returns immediately for polling. |
max_wait_ms | integer | Per-run wait cap when wait=true. Default 60000, max 110000. |
Single-run response
{
"run_id": "uuid-run-001",
"vapi_run_id": "vapi-run-xyz",
"status": "ended",
"passed": true,
"results": [
{
"status": "pass",
"messages": [
{ "role": "user", "content": "Hi, is this the law office?" },
{ "role": "assistant", "content": "Yes, this is Johnson Law — how can I help?" }
]
}
]
}Fan-out response
{
"total": 11,
"completed": 11,
"passed": 10,
"failed": 1,
"results": [
{ "eval_id": "uuid-eval-001", "name": "greeting.identifies_firm", "run_id": "uuid-run-001", "vapi_run_id": "vapi-run-xyz", "status": "ended", "passed": true },
{ "eval_id": "uuid-eval-008", "name": "price.no_quote", "run_id": "uuid-run-008", "vapi_run_id": "vapi-run-...", "status": "ended", "passed": false }
]
}/api/v1/evals/runPoll a run or list recent runsRequires evals:read. Polling refreshes from Vapi if the run is still in flight and persists the final result. Once status is "ended", results and passed are stable.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
run_id | string | KaiCalls run ID (from the POST response). |
vapi_run_id | string | Underlying Vapi run ID. |
agent_id | string | List the 50 most recent runs for an agent. |
Run statuses
| Parameter | Type | Description |
|---|---|---|
queued | status | Submitted to Vapi, not yet started. |
in-progress | status | Mock conversation is executing. |
ended | status | Finished — check passed and results. |
Notes
- During eval runs, the assistant runs with
metadata.test_mode = true. Tool handlers short-circuit destructive actions (no real SMS, no real bookings, no config writes). - One scenario tests one behavior. Keep judge prompts narrow and explicit: Pass if X. Fail if Y, Z, or W.
- Vapi rate-limits per org. Don't fan out 50+ evals back-to-back across multiple agents — space them out.
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", "agents:write", "numbers:read", "calls:read", "calls:write", "sms:read", "sms:write", "emails:read", "emails:write"],
"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 all standard scopes (agents, numbers, calls, sms, emails). |
{
"key": "kc_live_abc123def456...",
"id": "uuid-key123",
"prefix": "kc_live_abc1",
"name": "My Integration Key",
"scopes": ["agents:read", "agents:write", "numbers:read", "calls:read", "calls:write", "sms:read", "sms:write", "emails:read", "emails:write"],
"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 }Analytics
Aggregate analytics endpoints for dashboards, reports, and monitoring. All endpoints auto-scope to the API key's business(es).
/api/v1/analytics/dashboardDashboard summaryComprehensive snapshot: leads, calls, conversion rates, and top agents.
| Parameter | Type | Description |
|---|---|---|
days | integer | Look-back period in days (default: 30, max: 90) |
{
"period": { "days": 30, "since": "2026-02-16T00:00:00Z" },
"leads": {
"total": 142,
"by_status": { "new": 45, "contacted": 38, "qualified": 32, "converted": 18, "lost": 9 },
"conversion_rate": 35.21,
"recent": [{ "id": "uuid-1", "name": "Jane Doe", "status": "new", "source": "website", "created_at": "..." }]
},
"calls": {
"total": 387,
"completed": 351,
"total_duration_seconds": 48200,
"avg_duration_seconds": 125
},
"agents": {
"total": 3,
"top_by_calls": [{ "agent_id": "uuid-a1", "name": "Kai", "count": 215, "total_duration": 28000, "avg_duration": 130 }]
}
}/api/v1/analytics/callsCall analyticsCall volume, outcomes, and avg duration grouped by day or week.
| Parameter | Type | Description |
|---|---|---|
days | integer | Look-back period (default: 30, max: 90) |
group_by | string | "day" (default) or "week" |
agent_id | string | Filter by agent |
{
"period": { "days": 7, "since": "...", "group_by": "day" },
"summary": { "total_calls": 87, "total_duration_seconds": 11200, "avg_duration_seconds": 129 },
"timeline": [
{ "date": "2026-03-12", "total": 14, "completed": 12, "total_duration": 1800, "avg_duration_seconds": 129, "statuses": { "ended": 12, "no-answer": 2 } }
]
}/api/v1/analytics/funnelConversion funnelLead conversion funnel: new → contacted → qualified → converted. Requires leads:read scope.
| Parameter | Type | Description |
|---|---|---|
days | integer | Look-back period (default: 30, max: 90) |
source | string | Filter by lead source |
{
"period": { "days": 30, "since": "..." },
"total_leads": 142,
"lost": 9,
"funnel": [
{ "stage": "new", "count": 133, "rate_from_top": 93.66, "dropoff_rate": 0 },
{ "stage": "contacted", "count": 88, "rate_from_top": 61.97, "dropoff_rate": 33.83 },
{ "stage": "qualified", "count": 50, "rate_from_top": 35.21, "dropoff_rate": 43.18 },
{ "stage": "converted", "count": 18, "rate_from_top": 12.68, "dropoff_rate": 64 }
]
}/api/v1/analytics/agentsAgent performancePer-agent stats: call count, avg duration, lead conversion.
| Parameter | Type | Description |
|---|---|---|
days | integer | Look-back period (default: 30, max: 90) |
agent_id | string | Filter to a single agent |
{
"period": { "days": 30, "since": "..." },
"agents": [
{
"agent_id": "uuid-a1",
"agent_name": "Kai",
"calls": { "total": 215, "completed": 198, "avg_duration_seconds": 130, "total_duration_seconds": 28000 },
"leads": { "total": 89, "converted": 18, "conversion_rate": 20.22 }
}
]
}/api/v1/analytics/weeklyWeekly reportThis week vs. last week comparison with percent changes.
{
"period": {
"this_week": { "start": "2026-03-11T...", "end": "2026-03-18T..." },
"last_week": { "start": "2026-03-04T...", "end": "2026-03-11T..." }
},
"calls": {
"this_week": { "total": 87, "completed": 79, "total_duration_seconds": 11200, "avg_duration_seconds": 129 },
"last_week": { "total": 72, "completed": 65, "total_duration_seconds": 9100, "avg_duration_seconds": 126 },
"change": { "total": 20.83, "completed": 21.54, "avg_duration": 2.38 }
},
"leads": {
"this_week": { "total": 34, "converted": 6, "conversion_rate": 17.65 },
"last_week": { "total": 28, "converted": 4, "conversion_rate": 14.29 },
"change": { "total": 21.43, "converted": 50 }
},
"top_agents": [{ "agent_id": "uuid-a1", "agent_name": "Kai", "total_calls": 52, "total_duration_seconds": 6800 }]
}/api/v1/analytics/businessesBusiness performancePer-business breakdown for multi-tenant accounts.
| Parameter | Type | Description |
|---|---|---|
days | integer | Look-back period (default: 30, max: 90) |
{
"period": { "days": 30, "since": "..." },
"businesses": [
{
"business_id": "uuid-biz1",
"business_name": "Smith Law Firm",
"calls": { "total": 215, "completed": 198, "avg_duration_seconds": 130 },
"leads": { "total": 89, "converted": 18, "conversion_rate": 20.22 }
}
]
}Transcripts
/api/v1/transcriptsGet call transcriptsRecent call transcripts with summaries. Only returns calls that have a transcript.
| Parameter | Type | Description |
|---|---|---|
limit | integer | Max results (default: 20, max: 50) |
agent_id | string | Filter by agent |
after | string | Cursor pagination — created_at of last item |
days | integer | Look-back period (default: 30, max: 90) |
{
"transcripts": [
{
"call_id": "uuid-call1",
"conversation_id": "vapi-conv-123",
"agent_id": "uuid-a1",
"agent_name": "Kai",
"status": "ended",
"duration": 185,
"summary": "Caller inquired about pricing for estate planning services...",
"transcript": "Agent: Hello! How can I help you today?\nCaller: Hi, I'm looking for...",
"created_at": "2026-03-17T14:30:00Z"
}
],
"has_more": 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
Two ways to receive events from KaiCalls:
- Per-call webhooks — pass a
webhook_urlwhen callingPOST /v1/calls. Firescall.completed/call.failedfor that single call only. - Global webhooks — register one URL per business via
POST /api/v1/webhooksto receivelead.created,lead.scored, andcall.completedfor every event in that business. Recommended for partners.
Global webhook configuration
/api/v1/webhooksRegister a global webhook URLRequired scope: webhooks:write. The signing secret is returned once in the response — store it immediately, it cannot be retrieved later.
Request
curl -X POST https://www.kaicalls.com/api/v1/webhooks \
-H "Authorization: Bearer kc_live_xxxxx" \
-H "Content-Type: application/json" \
-d '{
"business_id": "uuid-biz456",
"webhook_url": "https://partner.example.com/webhooks/kaicalls"
}'{
"business_id": "uuid-biz456",
"webhook_url": "https://partner.example.com/webhooks/kaicalls",
"webhook_secret": "abc123...full-secret",
"webhook_secret_masked": "...a1b2c3d4",
"events": ["lead.created", "lead.scored", "call.completed"],
"message": "Webhook configured. Save the secret now — it will not be shown again."
}/api/v1/webhooksView current webhook configRequired scope: webhooks:read. When the API key spans multiple businesses, pass ?business_id=<uuid>. Returns the URL plus a masked secret only.
curl "https://www.kaicalls.com/api/v1/webhooks?business_id=uuid-biz456" \
-H "Authorization: Bearer kc_live_xxxxx"{
"business_id": "uuid-biz456",
"webhook_url": "https://partner.example.com/webhooks/kaicalls",
"webhook_secret_masked": "...a1b2c3d4",
"configured": true,
"events": ["lead.created", "lead.scored", "call.completed"]
}/api/v1/webhooks?business_id=<uuid>Remove the configured webhookRequired scope: webhooks:write.
Verifying signatures
Every outbound webhook (other than Discord URLs) is signed with HMAC-SHA256. Each request carries:
X-KaiCalls-Signature: t=<unix_timestamp>,v1=<hex_signature>
X-KaiCalls-Event: lead.createdThe signature is computed as HMAC_SHA256(secret, "<timestamp>." + raw_body). Example verification in Python:
import hmac, hashlib
def verify(secret, signature_header, body):
parts = dict(p.split('=', 1) for p in signature_header.split(','))
timestamp, signature = parts['t'], parts['v1']
expected = hmac.new(
secret.encode(),
f"{timestamp}.{body}".encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)Event payloads
lead.created
{
"event": "lead.created",
"timestamp": "2026-04-27T14:32:12Z",
"business_id": "uuid-biz456",
"data": {
"id": "uuid-lead123",
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "+15125550123",
"source": "inbound_call"
}
}lead.scored
{
"event": "lead.scored",
"timestamp": "2026-04-27T14:33:45Z",
"business_id": "uuid-biz456",
"data": {
"lead_id": "uuid-lead123",
"score": 82,
"confidence": "high",
"explanation": "Strong intent signals: requested pricing, asked about timeline.",
"factors": ["pricing_question", "timeline_question", "decision_maker"]
}
}call.completed
{
"event": "call.completed",
"timestamp": "2026-04-27T14:32:12Z",
"business_id": "uuid-biz456",
"data": {
"call_id": "uuid-call789",
"status": "completed",
"duration": 127,
"agent_name": "Kai",
"ended_reason": "customer-ended-call",
"recording_url": "https://...",
"summary": "Customer asked about pricing for 10-person team..."
}
}call.failed (per-call webhook only)
{
"event": "call.failed",
"call_id": "uuid-call789",
"status": "failed",
"error": "no_answer",
"timestamp": "2026-04-27T14:31:00Z"
}v1 limitations
- One webhook URL per business — use your own dispatcher for fan-out.
- Single delivery attempt with a 10-second timeout. Failures are logged in
webhook_logsbut not retried in v1. - No event filtering — every supported event fires to the configured URL.
- Discord webhook URLs (matching
discord.com/api/webhooks/) are auto-formatted as embed messages and not signed — Discord rejects custom headers on its webhook endpoint.
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 |
validation_error | 400 | Field validation failed (e.g. invalid email) |
email_exists | 409 | Email already registered (signup) |
number_on_dnc | 400 | Recipient is on the Do-Not-Call list (SMS) |
number_opted_out | 400 | Recipient has not opted in (SMS) |
sms_send_failed | 500 | Twilio failed to deliver the SMS |
vapi_error | 4xx/5xx | Vapi API call failed (create/update agent) |
call_failed | 500 | Vapi failed to initiate the call |
payment_required | 402 | x402 payment required (signup, when enabled) |
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 |
| POST /v1/signup | 5 requests/hour per IP |
| POST /v1/sms/send | 60 requests/minute |
| GET /v1/analytics/* | 120 requests/minute |
| GET /v1/transcripts | 120 requests/minute |
| All other 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 per-channel prompts
kai.agents.update(
id=agent.id,
inbound_prompt="Updated inbound instructions.",
outbound_prompt="Updated outbound instructions.",
sms_prompt="Updated SMS instructions."
)
# Update Vapi config directly (interruption, end-call, transcriber, etc.)
kai.agents.update(
id=agent.id,
vapi_config={
"stopSpeakingPlan": {
"numWords": 2,
"interruptionPhrases": ["stop", "wait", "hold on", "no"],
"acknowledgementPhrases": ["okay", "yeah", "right"]
},
"endCallPhrases": ["goodbye", "have a nice day"]
}
)
# Send an SMS
kai.sms.send(
to="+15125551234",
from_agent_id="uuid-abc123",
message="Hi John, following up on our call!",
lead_id="uuid-lead123"
)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 per-channel prompts
await kai.agents.update({
id: agent.id,
inboundPrompt: 'Updated inbound instructions.',
outboundPrompt: 'Updated outbound instructions.',
smsPrompt: 'Updated SMS instructions.',
});
// Send an SMS
await kai.sms.send({
to: '+15125551234',
fromAgentId: 'uuid-abc123',
message: 'Hi John, following up on our call!',
leadId: 'uuid-lead123',
});Need help? support@kaicalls.com