Skip to content

Telephony API

Endpoints for managing voice calls, campaigns, journals, and integrations. All endpoints require authentication via Bearer token (see REST API → Authentication).

Base URL: https://api.aispinner.io

Calls & Campaigns

Start a Dialer Campaign

http
POST /telephony/dialer/start
Authorization: Bearer <token>
Content-Type: application/json

{
  "workspace_id": 1,
  "node_id": "pbx_abc123",
  "numbers": ["+15551234567", "+15557654321"],
  "agent_node_id": "agent_xyz",
  "max_concurrent": 5
}

Starts a chunked campaign through the connected ElevenLabs (or Custom Voice) stack. The dialer splits numbers into chunks of max_concurrent and submits each chunk sequentially.

Response:

json
{ "call_id": "camp_abc123", "status": "running" }

Get Resumable Campaigns

http
GET /telephony/runs/resumable?workspace_id=1
Authorization: Bearer <token>

Lists campaigns that were paused and can be resumed. A paused campaign stores its remaining number list; resuming it picks up where it stopped.

List Runs (history)

http
GET /telephony/runs?workspace_id=1&node_id=pbx_abc123&limit=100
Authorization: Bearer <token>

Rename a Campaign

http
PATCH /telephony/runs/{call_id}/name
Authorization: Bearer <token>
Content-Type: application/json

{ "name": "Q2 outbound — VIP list" }

Get Call Events (poll/SSE)

http
GET /telephony/calls/{call_id}/events
Authorization: Bearer <token>

Returns the event stream for a call (status changes, transcript turns, completion). Prefer the Realtime Events WebSocket for push delivery instead of polling this.

Get Call Audio

http
GET /telephony/calls/{call_id}/audio?token=<token>

Returns the MP3 recording of a single call. The token is passed as a query parameter so this URL can be embedded in <audio> players.


Journal

Call history with transcripts, summaries, and audio links — used by the Journal block (ai.journal) and the PBX Monitor block.

All-Nodes Journal

http
GET /telephony/journal?workspace_id=1&limit=200
Authorization: Bearer <token>

Returns all calls across the user's workspaces. Filter by node_id, date_from, date_to, status (comma-separated, e.g. done,failed).

Response shape:

json
{
  "total": 42,
  "total_duration_sec": 8431,
  "max_id": 12345,
  "calls": [
    {
      "id": 12345,
      "call_id": "call_abc",
      "status": "done",
      "subscriber_phone": "+15551234567",
      "duration_sec": 142,
      "summary": "Customer agreed to a callback...",
      "transcript": [
        {"role": "agent", "message": "Hello!", "time_in_call_secs": 0},
        {"role": "user",  "message": "Hi.",     "time_in_call_secs": 1.2}
      ],
      "created_at": "2026-05-04T10:23:45Z",
      "conversation_id": "conv_xyz",
      "has_audio": true,
      "parent_call_id": "camp_parent_id"
    }
  ],
  "campaigns": [
    {
      "call_id": "camp_parent_id",
      "name": "VIP outreach",
      "status": "completed",
      "numbers_count": 50,
      "child_count": 50,
      "success_count": 38,
      "failed_count": 12,
      "total_duration_sec": 4200
    }
  ]
}

Per-Node Journal

http
GET /telephony/journal/{node_id}?workspace_id=1&limit=200
Authorization: Bearer <token>

Same shape, scoped to one PBX / Phone Number / Agent node.

Delta Pagination

Pass since_id=<max_id> to receive only records newer than that id — the journal is append-mostly, so this is a cheap incremental refresh:

http
GET /telephony/journal/{node_id}?workspace_id=1&since_id=12345
Authorization: Bearer <token>

The frontend Journal block uses this on every WebSocket journal.new event.

Single Call Detail

http
GET /telephony/journal/call/{call_id}
Authorization: Bearer <token>

Returns one call with full transcript and summary. Used by the PBX Monitor block when a row is expanded.


Settings

Get Telephony Settings

http
GET /telephony/settings
Authorization: Bearer <token>

Returns ARI / SIP defaults for Custom Voice mode.

Update Telephony Settings

http
PUT /telephony/settings
Authorization: Bearer <token>
Content-Type: application/json

{ "settings_json": { "default_provider_id": "ari_main" } }

Phone Provider Configuration

Per-node configuration for the inbound/outbound carrier assigned to an ai.phone_number block.

Get Phone Provider

http
GET /workspaces/{ws_id}/nodes/{node_id}/phone-provider
Authorization: Bearer <token>

Update Phone Provider

http
PUT /workspaces/{ws_id}/nodes/{node_id}/phone-provider
Authorization: Bearer <token>
Content-Type: application/json

{
  "provider": "twilio",
  "twilio_sid": "ACxxxxxxxx",
  "twilio_token": "<your token>",
  "twilio_phone": "+15551234567",
  "agent_node_id": "agent_abc"
}

provider is twilio or sip. SIP fields: sip_server, sip_user, sip_password, sip_port. Sensitive credentials are encrypted at rest (Fernet).

Delete Phone Provider

http
DELETE /workspaces/{ws_id}/nodes/{node_id}/phone-provider
Authorization: Bearer <token>

Import from ElevenLabs

http
POST /workspaces/{ws_id}/nodes/{node_id}/phone-provider/import
Authorization: Bearer <token>

Pulls the existing SIP/Twilio configuration from your linked ElevenLabs account into the AiSpinner node — saves manual re-entry of trunk credentials.


ElevenLabs Integration

List API Keys

http
GET /integrations/elevenlabs/keys
Authorization: Bearer <token>

Add API Key

http
POST /integrations/elevenlabs/keys
Authorization: Bearer <token>
Content-Type: application/json

{ "title": "My ElevenLabs", "key": "sk_..." }

The key is encrypted before storage; only the last 4 characters are returned to clients. Replace it by adding a new key and removing the old one.

Delete API Key

http
DELETE /integrations/elevenlabs/keys/{id}
Authorization: Bearer <token>

List Phone Numbers (SIP + Twilio)

http
GET /integrations/sip/phone-numbers
Authorization: Bearer <token>

Returns every phone number visible in your ElevenLabs account. The Phone Number block uses this for its SIP / Twilio tab.


Agents

List ElevenLabs Agents

http
GET /agents/elevenlabs/list
Authorization: Bearer <token>

Adopt an Existing ElevenLabs Agent

http
POST /agents/adopt
Authorization: Bearer <token>
Content-Type: application/json

{ "elevenlabs_agent_id": "agent_xyz", "node_id": "agent_node_abc" }

Pulls the agent's full configuration (name, voice, prompt, tools, model, voice settings, temperature, max_tokens, platform tools, safety rules, raw config) into a local AiSpinner record without creating a new agent on ElevenLabs side. Use this to bring already-configured agents onto your canvas non-destructively.

Test Agent

http
POST /integrations/elevenlabs/agents/test
Authorization: Bearer <token>
Content-Type: application/json

{ "elevenlabs_agent_id": "agent_xyz" }

WebCall (Browser-side Voice)

In-browser WebRTC bridging to a connected agent. Used by the demo "Try this agent" UI.

http
GET  /webcall/audio/{call_id}.mp3
POST /webcall/answered
POST /webcall/connected

These are mostly internal to the WebRTC flow — you usually don't call them directly.


Error Responses

Standard format (see REST API → Errors):

json
{ "detail": "campaign not found" }

Telephony-specific status codes:

  • 404 — Workspace, node, or call not found
  • 409 — Conflict (e.g. trying to start a campaign that's already running)
  • 422 — Invalid number format, missing agent connection
  • 429 — ElevenLabs rate-limit hit (system retries automatically; if you see this, your account quota is exhausted)

AiSpinner Documentation