client_secret your browser uses to open a /v3/realtime WebSocket. Browsers can’t set an Authorization header on a WebSocket constructor, so this endpoint exists to bridge the gap: the customer’s backend authenticates with its normal API key, optionally pre-binds session config fields, and returns the ticket to the browser.
See the Realtime voice guide for the end-to-end flow.
Authentication
Standard bearer auth with a project-scoped runtime API key. Management keys (opmak-…) are rejected.
Request body
| Field | Type | Description |
|---|---|---|
config | object | Optional. Any non-zero fields are bound to the ticket and cannot be overridden when the browser sends session.start or session.update. See session config reference. |
locked_fields | string[] | Optional. Names of config fields that must be enforced even when their value in config is the zero value — use this to force a boolean off (e.g. ["output_transcription"] with config.output_transcription = false prevents the browser from enabling it). Field names match the JSON tag on the config schema. A locked_fields list with no config at all is a valid “unconditionally forbid these” shape: each listed field is force-set to its zero value. Unknown field names are rejected with 400. |
ttl_seconds | integer | Optional. Ticket lifetime in seconds. Default 60, max 300. Values outside the range are clamped. |
Pre-binding
Bound fields win over whatever the browser sends — this is the safety guarantee tickets provide. Recommended minimum binding:model. Tighter setups also bind instructions, tools, and voice.
Response
| Field | Type | Description |
|---|---|---|
client_secret | string | Opaque single-use ticket. Treat as a short-lived bearer credential and discard after one WebSocket upgrade. |
expires_at | string (RFC3339) | UTC timestamp after which the ticket is rejected even if unused. |
ws_url | string | Optional. Pre-built WebSocket URL including the ticket query string. May be empty. |
Errors
| Status | Cause |
|---|---|
400 Bad Request | Invalid JSON; pre-bound model is unknown or not a realtime model; locked_fields contains a name that doesn’t match any config field (typo guard — error names the offending value). |
401 Unauthorized | Missing, non-runtime, or project-less API key. |
503 Service Unavailable | Realtime tickets not enabled on this deployment. |
Redemption
The browser presents the ticket on the WebSocket upgrade. Two transports are accepted; prefer the subprotocol header — credentials in the URL query string end up in access logs, browser history, andReferer headers, while the subprotocol header is request-only.
Recommended (subprotocol header):
401.
See also
- Realtime protocol — full WebSocket event vocabulary.
- Realtime voice guide — pre-binding, lifecycle, billing.