> ## Documentation Index
> Fetch the complete documentation index at: https://docs.opper.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Mint realtime ticket

> Server-to-server endpoint that mints a short-lived, single-use ticket for browser realtime sessions.

```
POST /v3/realtime-sessions
```

Returns a single-use `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](/capabilities/realtime#browser-ephemeral-tickets) for the end-to-end flow.

## Authentication

Standard bearer auth with a project-scoped runtime API key. Management keys (`opmak-…`) are rejected.

```
Authorization: Bearer <api-key>
```

## 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](/capabilities/realtime#configuring-the-session).                                                                                                                                                                                                                                                                                               |
| `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`.

```json theme={null}
{
  "config": {
    "model": "openai/gpt-realtime-2",
    "voice": "marin",
    "instructions": "You are a concise voice assistant.",
    "tools": [
      { "name": "lookup_order", "description": "...", "parameters": { /* ... */ } }
    ]
  },
  "ttl_seconds": 60
}
```

## 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.                          |

```json theme={null}
{
  "client_secret": "Z2VtaW5pLWFkLWhvY...",
  "expires_at": "2026-05-13T15:32:00Z",
  "ws_url": "wss://api.opper.ai/v3/realtime?ticket=Z2VtaW5pLWFkLWhvY..."
}
```

## 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, and `Referer` headers, while the subprotocol header is request-only.

**Recommended (subprotocol header):**

```
GET /v3/realtime
Sec-WebSocket-Protocol: opper-ticket.<client_secret>
```

```typescript theme={null}
new WebSocket("wss://api.opper.ai/v3/realtime", [`opper-ticket.${clientSecret}`]);
```

**Fallback (query parameter)** — only for environments that can't set a subprotocol:

```
GET /v3/realtime?ticket=<client_secret>
```

Replays return `401`.

## See also

* **[Realtime protocol](/v3-api-reference/realtime/protocol)** — full WebSocket event vocabulary.
* **[Realtime voice guide](/capabilities/realtime)** — pre-binding, lifecycle, billing.
