Cosmo Realtime SDK
ReferenceApi

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:use scope (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

FieldTypeRequiredDefaultDescription
type"session-config"no"session-config"Discriminator literal.
versionstringno"1.0"Protocol version for negotiation. Mismatched major versions are rejected (version_mismatch).
agentcatalog | inlinenoneutral inline agentThe persona — see the two variants below, discriminated on type.
sessionobjectno{}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.

FieldTypeRequiredDescription
type"catalog"yesDiscriminator.
namestringyesWorkspace-unique machine handle (hyphen slug). Unknown or cross-workspace names reject the start.
inputsRecord<string,string>noPer-run values substituted into the resolved prompt's {{key}} placeholders.
toolstool spec[]noClient-executed specs, server-tool opt-ins, and inline definitions, merged with the agent's attached tools.
voiceobjectnoPer-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.

FieldTypeRequiredDefaultDescription
type"inline"yesDiscriminator.
instructionsstringnoserver defaultSystem instructions. Length validated at start against the model's context budget (instructions_too_long).
modelstringnoserver defaultConcrete model within the provider named by model_options. Unknown or unavailable values are rejected (model_unavailable).
model_optionsobjectnoprovider defaultsProvider-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).
voiceobjectnoupstream picksname (provider-specific prebuilt voice id) and speaking_style ("how to speak" text, as on the catalog variant).
audioobjectnoall defaultsThe audio pipeline: output (bool, default truefalse 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).
toolstool spec[]nononeTool set for the session — see Tool specs.
greetingstringnowait for userOpening 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.
hooksSilenceTimeout[]nononeDeclarative 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:

kindFieldsDescription
clientname, 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.
servernameOpt-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

FieldTypeDefaultDescription
max_session_secondsint (60–14400)server limitRequested 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_recordingboolrecordsfalse writes no recording artifacts for this run. See Recording and privacy.
experimental.resume_session_idUUIDResume 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"
}
FieldTypeDescription
livekit_urlstringLiveKit room URL to connect to.
tokenstringShort-lived participant join token. Do not cache it.
room_namestringLiveKit room name.
session_idstringServer-minted session identifier. Pass back as experimental.resume_session_id to resume; include in support requests.
timingsobjectOptional 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
  }
}
FieldDescription
typeError family: api_error, validation_error, or internal_error.
codeMachine-readable slug for typed rejections; absent otherwise.
messageHuman-readable reason.
errorsField-level violations (loc, type, msg) on validation_error responses. Payload values are never echoed back.

Notable rejections:

Statuscode / typeCause
401 / 403api_errorCredential absent, invalid, or lacking the realtime:use scope.
400version_mismatchClient protocol major version incompatible with the server.
422validation_errorBody 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.
422unknown_server_toolA kind: "server" tool name the server doesn't publish.
422invalid_tool_configAn inline tool definition failed semantic checks (queue not in workspace, name conflict, credential gating).
422model_unavailableUnknown model id, or one not available to this workspace/server.
422instructions_too_longinstructions exceeds the model's budget.
503Realtime 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

EndpointPurpose
POST /api/v1/external/realtime/session/{session_id}/dialDial 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/tokenMint a project-scoped end-user JWT from an API key (external_user_idjwt, expires_at). See End-user credentials.

Both take the same Bearer auth; the realtime endpoints require realtime:use, token minting requires user_tokens:mint.

On this page