Create a WebSocket call
POST /api/v1/call (no type) with transport=websocket — create a call backed by a raw-PCM WebSocket. Two-step flow — create the call, then connect /ws/audio/{callId}.
POST /api/v1/callCreate a call backed by a raw-PCM WebSocket stream. Returned audioUrl is the relative path to the WS endpoint — open it from your client and audio flows immediately, no SDP/ICE.
This is the right choice for server-side audio pipelines (BYO-SIP bridges, IVR integrations, audio file playback bots). Browsers should use the WebRTC mode instead because it handles echo cancellation, NAT traversal, and jitter buffering for you.
Authentication
X-API-Key: zsk_...Request body
type is omitted for the WebSocket path — that's what tells the dispatcher to use the WebSocket transport handler.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
transport | "websocket" | — | "websocket" | Selects raw-PCM WS transport. Default — omit to get this mode. |
agentId | string | one of two | — | A saved agent's UUID. Validated as a string only (not strict UUID, unlike the v2 type=outbound path). |
agent | object | one of two | — | Inline transient agent — a full Agent Config; at minimum name + llm: { provider, model }. Provide exactly one of agentId / agent. |
contextVariables | {string: string} | — | — | Substituted into {{key}} placeholders before the call starts. |
Lifecycle webhook lives inside the agent config's webhook field (saved or inline). See Webhooks for the shape.
Audio format (fixed)
The wire format is fixed — there is nothing to declare on the request:
| Direction | Format |
|---|---|
| You → agent (microphone) | Raw 16-bit signed little-endian PCM, mono, 16 000 Hz |
| Agent → you (speech) | Raw 16-bit signed little-endian PCM, mono, 24 000 Hz |
The agent's voice is synthesized at 24 kHz and delivered to you untouched, so you receive full generation quality. If you're bridging to a narrowband system (e.g. a SIP trunk at 8 kHz), resample on your side.
Response
Returns HTTP 201 Created.
{
"callId": "call_4e4e571f8c9b3cf8e99d",
"audioUrl": "/api/v1/ws/audio/call_4e4e571f8c9b3cf8e99d",
"transport": "websocket",
"status": "pending"
}| Field | Type | Description |
|---|---|---|
callId | string | zoxaAI's internal id. Stable across the lifetime of the call. |
audioUrl | string | Relative path to the WS endpoint. Prepend wss://dashboard.zoxa.ai to connect. |
transport | "websocket" | Echo. |
status | "pending" | Call row created but not yet active. Becomes "active" when you connect the WS. |
Examples
1. Create the call
curl -X POST https://dashboard.zoxa.ai/api/v1/call \
-H "X-API-Key: zsk_..." \
-H "Content-Type: application/json" \
-d '{
"transport": "websocket",
"agentId": "550e8400-e29b-41d4-a716-446655440000"
}'const res = await fetch("https://dashboard.zoxa.ai/api/v1/call", {
method: "POST",
headers: {
"X-API-Key": "zsk_...",
"Content-Type": "application/json",
},
body: JSON.stringify({
transport: "websocket",
agentId: "550e8400-e29b-41d4-a716-446655440000",
}),
});
const { callId, audioUrl } = await res.json();import httpx
resp = httpx.post(
"https://dashboard.zoxa.ai/api/v1/call",
headers={"X-API-Key": "zsk_..."},
json={
"transport": "websocket",
"agentId": "550e8400-e29b-41d4-a716-446655440000",
},
)
data = resp.json()
call_id, audio_url = data["callId"], data["audioUrl"]2. Connect the WebSocket
wss://dashboard.zoxa.ai/api/v1/ws/audio/{callId}?api_key=zsk_...Full wire protocol on the WebSocket Protocol page.
What zoxaAI does after the 201
- Resolves the agent config (persistent or transient), substitutes
contextVariables, and writes it intoconfig_snapshoton the calls row. - Validates inline tools against your tool catalog. Invalid tools fail the request with
400and leave atransport=webrow tagged with the validation error for audit. - Returns
audioUrl— no pipeline runs until you connect the WS.
Errors
| Status | detail | When |
|---|---|---|
400 | "No organization selected" | Auth missing org context. |
400 | Pydantic message | Mode constraints failed (agentId + agent both set, etc.). |
400 | { "error": "Tool validation failed", "details": [...] } | Inline tool references something invalid (missing function, bad schema). |
400 | { "error": "Reference validation failed", "details": [...] } | Inline tool references a file/agent/etc. that doesn't exist in your org. |
404 | "Agent not found" | agentId doesn't resolve in your org. |
400 | validation object ({ error, details }) | Schema or cross-field validation failed — a missing required field (name, llm), an unknown field (extra="forbid"), or a mode rule (agentId + agent both set, or neither). Same object shape as the tool-validation rows above. |
Related
- WebSocket Protocol — wire spec (binary + text frames)
- WebRTC mode — browser-friendly alternative
GET /calls/{callId}— fetch transcript, recordings, latency stats after the call ends
Register an inbound phone number
POST /api/v1/call with type=inbound — bind a phone number to an agent. Last-write-wins upsert; idempotent re-registration.
Create a WebRTC call
POST /api/v1/call with transport=webrtc — create a browser call over SmallWebRTC. Complete the SDP offer/answer handshake at the returned offerUrl.