POST /session/start
REST endpoint to start a Cosmo Realtime session and mint the LiveKit join credentials.
POST /api/v1/external/realtime/session/start
Starts a realtime session from a session-config body and returns the LiveKit join credentials. The response contains everything the client needs to join the room; all further control traffic rides the wire protocol on the room's data channel.
The SDKs call this internally (agent.start(), RealtimeSession.start). Use the raw endpoint only for custom integrations.
Authentication
Bearer token in the Authorization header — either of:
- a workspace API key carrying the
realtime:usescope (server-side secret), or - a minted end-user JWT (from
POST /api/v1/external/auth/token), safe on end-user devices.
Authorization: Bearer cosmo_...See API keys, Scopes, and End-user credentials.
Request
POST /api/v1/external/realtime/session/start
Content-Type: application/json
Authorization: Bearer cosmo_...The body is the external protocol's session-config payload: an agent block (the persona) plus a session block (per-run options).
{
"type": "session-config",
"version": "1.0",
"agent": {
"type": "inline",
"instructions": "You are a concise support agent.",
"voice": { "name": "Puck" },
"tools": [{ "kind": "server", "name": "cosmo.web_search" }]
},
"session": { "max_session_seconds": 1800 }
}Top level
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
type | "session-config" | no | "session-config" | Discriminator literal. |
version | string | no | "1.0" | Protocol version for negotiation. Mismatched major versions are rejected (version_mismatch). |
agent | catalog | inline | no | neutral inline agent | The persona — see the two variants below, discriminated on type. |
session | object | no | {} | Per-run options — see Session params. |
agent — catalog variant
Run a workspace catalog agent by machine handle; the stored config runs verbatim. Only per-run ride-alongs may accompany the launch — any other field is a schema error (extra="forbid"), not a runtime rejection.
| Field | Type | Required | Description |
|---|---|---|---|
type | "catalog" | yes | Discriminator. |
name | string | yes | Workspace-unique machine handle (hyphen slug). Unknown or cross-workspace names reject the start. |
inputs | Record<string,string> | no | Per-run values substituted into the resolved prompt's {{key}} placeholders. |
tools | tool spec[] | no | Client-executed specs, server-tool opt-ins, and inline definitions, merged with the agent's attached tools. |
voice | object | no | Per-run voice: name (the override id — the one cosmetic exception to "stored config runs verbatim") and speaking_style ("how to speak" text appended to the system prompt after the persona, max 8192 chars). |
agent — inline variant
Define the persona in the request. Catalog-only fields (name, inputs) are structurally absent here.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
type | "inline" | yes | — | Discriminator. |
instructions | string | no | server default | System instructions. Length validated at start against the model's context budget (instructions_too_long). |
model | string | no | server default | Concrete model within the provider named by model_options. Unknown or unavailable values are rejected (model_unavailable). |
model_options | object | no | provider defaults | Provider-scoped knobs, discriminated on provider: gemini (temperature, max_output_tokens, thinking_level), cosmo_voice_ultravox (temperature, turn_endpoint_delay_seconds), openai, cosmo_voice_personaplex (no knobs today). |
voice | object | no | upstream picks | name (provider-specific prebuilt voice id) and speaking_style ("how to speak" text, as on the catalog variant). |
audio | object | no | all defaults | The audio pipeline: output (bool, default true — false runs the session text-only; rejected when the resolved model is speech-to-speech only), noise_cancellation (bool, default false — background-voice cancellation on the user's inbound audio), and ambience (object — background bed on the assistant's output; presence enables it, with track and gain_db -60..0, default -15). |
tools | tool spec[] | no | none | Tool set for the session — see Tool specs. |
greeting | string | no | wait for user | Opening line the assistant speaks as soon as the model session opens. Max 4000 chars. A resumed session never re-greets. |
interruption_sensitivity | "low" | "default" | "high" | no | "default" | How readily user audio barges in over the assistant. |
hooks | SilenceTimeout[] | no | none | Declarative server hooks (max 16): {trigger: "user.speech.timeout", timeout_seconds, action, max_count?, reset_mode?, name?} with a say or end_call action. See Hooks. |
Tool specs
Each entry in tools is discriminated on kind:
kind | Fields | Description |
|---|---|---|
client | name, description, parameters (JSON Schema, restricted dialect) | Client-executed tool, self-described at start. Invalid specs are soft-rejected per spec and echoed on ready.rejected_tools. |
server | name | Opt-in to a server-executed tool by dot-namespaced name (e.g. cosmo.web_search, cosmo.end_call). Unknown names reject the start (unknown_server_tool). |
Session params
| Field | Type | Default | Description |
|---|---|---|---|
max_session_seconds | int (60–14400) | server limit | Requested wall-clock cap. The server resolves the effective cap as the minimum of this and its own limits; the effective value is echoed on ready. |
store_recording | bool | records | false writes no recording artifacts for this run. See Recording and privacy. |
experimental.resume_session_id | UUID | — | Resume the named prior session. Experimental — may change without a version bump. |
Response
200 OK
{
"livekit_url": "wss://your-livekit-server.livekit.cloud",
"token": "eyJhbGciOiJIUzI1NiIs...",
"room_name": "session-abc123-xyz",
"session_id": "8f7e6d5c-4b3a-2190-aaaa-bbbbccccdddd"
}| Field | Type | Description |
|---|---|---|
livekit_url | string | LiveKit room URL to connect to. |
token | string | Short-lived participant join token. Do not cache it. |
room_name | string | LiveKit room name. |
session_id | string | Server-minted session identifier. Pass back as experimental.resume_session_id to resume; include in support requests. |
timings | object | Optional server-side session-start phase breakdown (ms). |
After joining the room, wait for the ready frame on the data channel before the first send — see Lifecycle.
Errors
Every non-2xx response carries the same envelope:
{
"error": {
"type": "api_error",
"code": "unknown_server_tool",
"message": "Unknown server tool(s): cosmo.web_serch.",
"errors": null
}
}| Field | Description |
|---|---|
type | Error family: api_error, validation_error, or internal_error. |
code | Machine-readable slug for typed rejections; absent otherwise. |
message | Human-readable reason. |
errors | Field-level violations (loc, type, msg) on validation_error responses. Payload values are never echoed back. |
Notable rejections:
| Status | code / type | Cause |
|---|---|---|
| 401 / 403 | api_error | Credential absent, invalid, or lacking the realtime:use scope. |
| 400 | version_mismatch | Client protocol major version incompatible with the server. |
| 422 | validation_error | Body failed schema validation — including the legacy flat shape (persona fields at the top level instead of under agent / session), and unknown fields on a catalog agent block. |
| 422 | unknown_server_tool | A kind: "server" tool name the server doesn't publish. |
| 422 | invalid_tool_config | An inline tool definition failed semantic checks (queue not in workspace, name conflict, credential gating). |
| 422 | model_unavailable | Unknown model id, or one not available to this workspace/server. |
| 422 | instructions_too_long | instructions exceeds the model's budget. |
| 503 | — | Realtime backend temporarily unavailable or at capacity. Retry with backoff. |
Client-tool specs fail soft: an individual invalid kind: "client" spec never rejects the start — it is dropped and echoed with a reason on the ready frame's rejected_tools. Server-tool and inline-tool problems fail the whole start instead.
Example — curl
curl -X POST https://app.askcosmo.ai/api/v1/external/realtime/session/start \
-H "Authorization: Bearer cosmo_..." \
-H "Content-Type: application/json" \
-d '{
"agent": {
"type": "catalog",
"name": "support-agent",
"inputs": { "caller_name": "Sam" }
},
"session": { "max_session_seconds": 1800 }
}'Sibling endpoints
| Endpoint | Purpose |
|---|---|
POST /api/v1/external/realtime/session/{session_id}/dial | Dial an E.164 number into a running session as a SIP participant (phone_number, optional caller_number; returns dial_id). See Telephony. |
POST /api/v1/external/auth/token | Mint a project-scoped end-user JWT from an API key (external_user_id → jwt, expires_at). See End-user credentials. |
Both take the same Bearer auth; the realtime endpoints require realtime:use, token minting requires user_tokens:mint.