Create a session
const url = 'https://example.com/v1/sessions';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"avatar_id":"av_demo","idle_timeout_seconds":30,"max_duration_seconds":600,"metadata":{"customer_session_id":"abc123"},"quality":"standard","transport":{"room_name":"demo-room","type":"livekit","url":"wss://my-app.livekit.cloud","worker_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/sessions \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "avatar_id": "av_demo", "idle_timeout_seconds": 30, "max_duration_seconds": 600, "metadata": { "customer_session_id": "abc123" }, "quality": "standard", "transport": { "room_name": "demo-room", "type": "livekit", "url": "wss://my-app.livekit.cloud", "worker_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } }'Allocate a worker and begin a realtime avatar session.
Returns immediately with status: queued; poll GET /v1/sessions/{id} (or wait for the session.first_frame webhook) to see the worker pick the job up. Sessions count against your plan’s concurrency cap; a 503 response with code=at_capacity and a Retry-After header means there are no free workers right now.
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”Request body for POST /v1/sessions.
object
av_… prefixed ULID. Must reference an avatar the caller’s API key can access.
End the session automatically after this many seconds without received audio. Only applies to transports that observe inbound audio; ignored otherwise.
Hard ceiling on session duration; worker terminates on hit.
Quality tier; drives the billing multiplier.
BYO LiveKit transport.
The customer owns the room and mints worker_token; we never touch
their LiveKit API key or secret.
object
How the worker receives audio. See LiveKitAudioSource.
Name of the LiveKit room the worker should join.
If True and no participant joins within 10 s, the worker falls back to a synthetic sine generator. Demo aid only; default off.
Customer’s LiveKit URL, e.g. wss://my-app.livekit.cloud.
Identity the worker presents inside the room.
Short-lived JWT the customer mints authorizing our worker to join the room. Write-only. Never echoed back on read; GET /v1/sessions/{id}.transport.worker_token returns the string "[redacted]".
Reserved — raw WebSocket transport. Not yet available.
Listed in the schema so existing clients keep parsing future responses,
but POST /v1/sessions rejects it today with
invalid_request / code=transport.unsupported.
object
Reserved — Pipecat integration. Not yet available.
Rejected at create-time today; see WebSocketTransportConfig.
object
Responses
Section titled “ Responses ”Successful Response
Public session resource — what GET /v1/sessions/{id} returns.
object
Populated when Session.status == failed.
object
Stable lower_snake_case subcode.
Human-readable summary; not stable.
sess_… prefixed ULID.
Output quality tier. Drives the credit billing multiplier.
standard is the default for new sessions. mock is reserved for
internal tests and the bring-up runtime (no real model loaded); it
bills as standard when it appears on a real session.
Public session lifecycle.
Terminal states are ended, failed, canceled. ending is the
graceful-drain state while a worker finishes publishing in-flight frames.
BYO LiveKit transport.
The customer owns the room and mints worker_token; we never touch
their LiveKit API key or secret.
object
How the worker receives audio. See LiveKitAudioSource.
Name of the LiveKit room the worker should join.
If True and no participant joins within 10 s, the worker falls back to a synthetic sine generator. Demo aid only; default off.
Customer’s LiveKit URL, e.g. wss://my-app.livekit.cloud.
Identity the worker presents inside the room.
Short-lived JWT the customer mints authorizing our worker to join the room. Write-only. Never echoed back on read; GET /v1/sessions/{id}.transport.worker_token returns the string "[redacted]".
Reserved — raw WebSocket transport. Not yet available.
Listed in the schema so existing clients keep parsing future responses,
but POST /v1/sessions rejects it today with
invalid_request / code=transport.unsupported.
object
Reserved — Pipecat integration. Not yet available.
Rejected at create-time today; see WebSocketTransportConfig.
object
Live usage counters embedded in the Session resource.
Eventually-consistent — lags the latest worker heartbeat by up to
one interval. Canonical billing data lives in UsageEvent.
object
Example
{ "avatar_id": "av_demo", "created_at": "2026-05-25T19:00:00.123Z", "first_frame_at": "2026-05-25T19:00:02.001Z", "id": "sess_01HXY5K8E7QYG3X8Z6N9R7S0VR", "idle_timeout_seconds": 30, "max_duration_seconds": 600, "metadata": { "customer_session_id": "abc123" }, "object": "session", "quality": "standard", "started_at": "2026-05-25T19:00:01.456Z", "status": "running", "transport": { "audio_source": "data_stream", "room_name": "demo-room", "type": "livekit", "url": "wss://my-app.livekit.cloud" }, "usage": { "billable_seconds": 12, "frames": 300 }}Invalid request body.
Wire format for every non-2xx public API response.
object
Concrete error returned inside ApiErrorEnvelope.error.
object
Stable machine-readable subcode (lower_snake_case). SDKs should switch on this, not message.
Human-readable summary. Not stable; do not parse.
Echo of the X-Request-Id response header for support.
Top-level error categories. Maps roughly to HTTP status.
code (a free-form lower_snake_case string on ApiError) is the
machine-readable subcode SDKs should switch on; type is the broad
category.
Example
{ "error": { "code": "transport.unsupported", "message": "transport.type=pipecat is reserved and not yet available", "param": "transport.type", "request_id": "req_01HXY5K8E7QYG3X8Z6N9R7S0VR", "type": "invalid_request" }}Missing or invalid API key.
Wire format for every non-2xx public API response.
object
Concrete error returned inside ApiErrorEnvelope.error.
object
Stable machine-readable subcode (lower_snake_case). SDKs should switch on this, not message.
Human-readable summary. Not stable; do not parse.
Echo of the X-Request-Id response header for support.
Top-level error categories. Maps roughly to HTTP status.
code (a free-form lower_snake_case string on ApiError) is the
machine-readable subcode SDKs should switch on; type is the broad
category.
Example
{ "error": { "code": "transport.unsupported", "message": "transport.type=pipecat is reserved and not yet available", "param": "transport.type", "request_id": "req_01HXY5K8E7QYG3X8Z6N9R7S0VR", "type": "invalid_request" }}Conflicting state (e.g. avatar not ready).
Wire format for every non-2xx public API response.
object
Concrete error returned inside ApiErrorEnvelope.error.
object
Stable machine-readable subcode (lower_snake_case). SDKs should switch on this, not message.
Human-readable summary. Not stable; do not parse.
Echo of the X-Request-Id response header for support.
Top-level error categories. Maps roughly to HTTP status.
code (a free-form lower_snake_case string on ApiError) is the
machine-readable subcode SDKs should switch on; type is the broad
category.
Example
{ "error": { "code": "transport.unsupported", "message": "transport.type=pipecat is reserved and not yet available", "param": "transport.type", "request_id": "req_01HXY5K8E7QYG3X8Z6N9R7S0VR", "type": "invalid_request" }}Request body failed validation.
Wire format for every non-2xx public API response.
object
Concrete error returned inside ApiErrorEnvelope.error.
object
Stable machine-readable subcode (lower_snake_case). SDKs should switch on this, not message.
Human-readable summary. Not stable; do not parse.
Echo of the X-Request-Id response header for support.
Top-level error categories. Maps roughly to HTTP status.
code (a free-form lower_snake_case string on ApiError) is the
machine-readable subcode SDKs should switch on; type is the broad
category.
Example
{ "error": { "code": "transport.unsupported", "message": "transport.type=pipecat is reserved and not yet available", "param": "transport.type", "request_id": "req_01HXY5K8E7QYG3X8Z6N9R7S0VR", "type": "invalid_request" }}Rate limit exceeded.
Wire format for every non-2xx public API response.
object
Concrete error returned inside ApiErrorEnvelope.error.
object
Stable machine-readable subcode (lower_snake_case). SDKs should switch on this, not message.
Human-readable summary. Not stable; do not parse.
Echo of the X-Request-Id response header for support.
Top-level error categories. Maps roughly to HTTP status.
code (a free-form lower_snake_case string on ApiError) is the
machine-readable subcode SDKs should switch on; type is the broad
category.
Example
{ "error": { "code": "transport.unsupported", "message": "transport.type=pipecat is reserved and not yet available", "param": "transport.type", "request_id": "req_01HXY5K8E7QYG3X8Z6N9R7S0VR", "type": "invalid_request" }}No worker capacity; retry later.
Wire format for every non-2xx public API response.
object
Concrete error returned inside ApiErrorEnvelope.error.
object
Stable machine-readable subcode (lower_snake_case). SDKs should switch on this, not message.
Human-readable summary. Not stable; do not parse.
Echo of the X-Request-Id response header for support.
Top-level error categories. Maps roughly to HTTP status.
code (a free-form lower_snake_case string on ApiError) is the
machine-readable subcode SDKs should switch on; type is the broad
category.
Example
{ "error": { "code": "transport.unsupported", "message": "transport.type=pipecat is reserved and not yet available", "param": "transport.type", "request_id": "req_01HXY5K8E7QYG3X8Z6N9R7S0VR", "type": "invalid_request" }}