API documentation

Create AI agents, buy numbers and place calls over REST. Get a key from the Developers page.

Authentication

Send your key as a Bearer token. Use ak_test_… keys while building: no real calls are made.

Base URL: https://api-production-9c6d.up.railway.app/api/v1
Authorization: Bearer ak_test_...

Errors are {"detail": "..."} with a standard HTTP status. A 429 includes Retry-After.

Place a call

Send an Idempotency-Key so a network retry never dials twice.

curl -X POST https://api-production-9c6d.up.railway.app/api/v1/calls \
  -H "Authorization: Bearer ak_test_..." \
  -H "Idempotency-Key: 7d1c0c5e-..." \
  -H "Content-Type: application/json" \
  -d '{"direction":"outbound","to_number":"+919876543210","from_number":"+14155550100","ai_agent_id":"<id>"}'

Inbound calls

Customers call a number you own; the AI agent attached to it answers. Provision or register a number, attach an agent, then subscribe to call.started, call.ended and call.transcript_ready webhooks to get each call and its transcript. Poll GET /calls?direction=inbound if you prefer. A number with no agent rejects calls cleanly.

curl -X PATCH https://api-production-9c6d.up.railway.app/api/v1/phone-numbers/<number id> \
  -H "Authorization: Bearer ak_live_..." \
  -H "Content-Type: application/json" \
  -d '{"ai_agent_id":"<agent id>"}'

Scopes: numbers:read, numbers:write (also lets the key edit agents), calls:read.

Testing without dialling

A ak_test_ key never reaches the phone network. POST /calls with either direction returns a completed call and fires call.started and call.ended (with test_mode: true) to your webhooks, so you can build and verify your handlers first.

Endpoints

POST/callsPlace an outbound AI call (test keys: simulate either direction)
GET/calls?direction=inbound|outbound&status=…List and filter calls
GET/calls/{id}/transcriptTurn-by-turn transcript
POST/ai-agentsCreate an AI agent (prompt, language, voice)
PATCH/ai-agents/{id}Update or activate an agent
PUT/ai-agents/{id}/toolsEnable tools such as transfer_call or create_ticket
GET/phone-numbers/availableSearch numbers you can buy
POST/phone-numbers/provisionBuy a number and attach an agent (real cost)
POST/phone-numbersRegister a number you already own
PATCH/phone-numbers/{id}Route inbound calls to another agent, or none
DELETE/phone-numbers/{id}Stop routing a number (does not release it at Twilio)
POST/customersCreate a customer
POST/campaignsCreate an outbound campaign
POST/campaigns/{id}/launchStart dialling a campaign

Webhooks

Events: call.startedcall.endedcall.transcript_readyticket.createdmessage.receivedcampaign.completed

Each delivery has X-Timestamp and X-Signature (HMAC-SHA256 of timestamp.body with your signing secret). Reject anything older than 5 minutes. Failed deliveries are retried with backoff.

const expected = crypto
  .createHmac("sha256", SIGNING_SECRET)
  .update(`${req.headers["x-timestamp"]}.${rawBody}`)
  .digest("hex")
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-signature"]))) reject()

Calling Indian numbers for commercial purposes requires TRAI DLT registration and DND scrubbing. You are responsible for consent.