Create a session
POST /sessions is authenticated with a bearer login session (not an API key) and sessions:write, because the session is attached to the logged-in user.
To place an outbound phone call from your backend, use
POST /calls instead — it takes an API key, and adds retries, calling windows and idempotency. POST /sessions is the browser/dashboard path.
A session runs the agent’s saved config, the same config API calls and campaigns use. A
config field is refused with 400. To try a change, edit a draft agent, or clone a published one, and start a session with it.
The response is a SessionRecord that includes the participant_token — this is the only place the token is returned. Connect it to room_name at livekit_url with a voice client to join the call.
Web vs outbound phone
- Web — the browser participant connects to the room and talks to the agent. The token grants publish + subscribe.
- Outbound phone — the agent dials the recipient over SIP; the token is subscribe-only (the human is on the phone leg). Requires an outbound SIP trunk to be configured on the deployment.
One test call at a time per workspace. A session is for trying an agent — in the browser or on a real phone — so by default a workspace runs one at a time, whichever kind it is. Starting another while one is live returns
409 conflict — “A test call is already running in this workspace” — and starts nothing; end the running call, or wait for it to finish, and try again. (A 409 saying too many test calls are running across the platform is rare; retry in a minute.) While it runs, the test call also counts toward your workspace’s concurrent-call limit, so a POST /calls call or a campaign dial waiting at that limit starts once it ends. A web session whose browser never connects ends by itself within about a minute and a half and frees the slot.Status lifecycle
ended_atis set on the transition to any terminal status.- Long-running
activesessions are swept toexpiredafter the session TTL (default 24h). - Terminal status is frozen. Once a session is
ended,failed, orexpired, itsstatusandended_atstop changing. APATCH /sessions/{id}/statuson a terminal session is a silent no-op —200with the record unchanged, not an error. - Ending tears down the room.
POST /sessions/{id}/end(or a PATCH that drives the session terminal) deletes the media room, so an explicit end actually stops a call that would otherwise keep billing — and frees the workspace’s test-call slot straight away.
Manage a session
GET /sessions is paged: pass ?limit= (default 20, max 100) and ?offset=, and read pagination.total from the { "data": [...], "pagination": { ... } } envelope.Results
When a call ends, the agent posts a session-update webhook with the transcript and a session report. The transcript and the call’s cost (in retail credits) are recorded on the session record. The underlying LLM usage — token counts and the per-model/provider breakdown — is platform-internal COGS detail used only for billing; it is not returned onGET /sessions reads. For production, register an outbound webhook and receive call_completed rather than polling.
Two fields are written during that finalize step:
outcome— how the call ended:completed,no_media,no_answer,busy,declined,failed,user_disconnectedorexpired. It’s the same vocabulary campaigns use to decide retries and analytics uses to aggregate.sentiment—positive,negativeorneutral, and only for agents that opted into sentiment analysis.
Webhooks & Events
Receive transcript and cost.
Campaigns
Run sessions in bulk.

