> This page is for Gateway.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.merge.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.merge.dev/_mcp/server.

# Live voice

> Connect to OpenAI GPT-Live (openai/gpt-live-1) through Gateway at wss://api-gateway.merge.dev/v1/live/sessions: authentication, session.start, delegation, per-second billing, limits, and close codes.

`openai/gpt-live-1` is a full-duplex voice model that listens and speaks at once, for voice agents and live assistants. Gateway relays the OpenAI Live protocol at `wss://api-gateway.merge.dev/v1/live/sessions` unchanged, so the [OpenAI Live event reference](https://developers.openai.com/api/docs/guides/live-conversations) applies as written.

## Connect

Send your Gateway key on the handshake (so connect from your server), then send `session.start` with the model, which goes in that event, not the URL. Wait for `session.started` before sending audio.

**`Python`**

```python title="Python"
import asyncio, base64, json, os
import websockets

async def main():
    async with websockets.connect(
        "wss://api-gateway.merge.dev/v1/live/sessions",
        additional_headers={"Authorization": f"Bearer {os.environ['MERGE_GATEWAY_API_KEY']}"},
    ) as ws:
        await ws.send(json.dumps({
            "type": "session.start",
            "session": {"model": "openai/gpt-live-1", "audio": {"format": {"type": "audio/pcm", "rate": 24000}}},
        }))
        async for frame in ws:
            event = json.loads(frame)
            if event["type"] == "session.started":
                chunk = base64.b64encode(bytes(960)).decode()  # 20 ms of 24 kHz PCM16; stream your mic here
                await ws.send(json.dumps({"type": "session.input_audio.append", "audio": chunk}))
                await ws.send(json.dumps({"type": "session.close"}))
            elif event["type"] == "session.output_audio.delta":
                pass  # base64 PCM16 to play back
            elif event["type"] == "session.closed":
                print("billed seconds:", event["usage"]["seconds"])
                break

asyncio.run(main())
```

Audio travels as base64 PCM in JSON text frames. To end a session, send `session.close` and read until `session.closed`, which carries the final `usage.seconds`.

## Delegation

Client delegation is the default: your app receives `session.delegation.created` and answers with `session.commentary.append` or `session.thinking.append`. With `"delegation": {"type": "responses", "responses": {"model": "..."}}`, OpenAI runs the backend model and streams its events as `response.event`. Gateway checks that your organization and key may use that model at `session.start` and on any `session.update` that changes it.

## Pricing

Sessions bill per second at the route's `input_per_second` rate, with no rounding to a whole minute. The billed time is OpenAI's `usage.seconds`, including silence and delegated work. If your client disconnects without `session.close`, Gateway closes the session upstream and still bills the final duration. Backend-model tokens under Responses delegation bill separately at that model's rates.

## Reference

| Limit                                     | Value                                                               |
| ----------------------------------------- | ------------------------------------------------------------------- |
| Maximum session length                    | 2 hours, or OpenAI's `session.started.session.expires_at` if sooner |
| Idle timeout (no events from your client) | 15 minutes                                                          |
| Time to send `session.start`              | 30 seconds                                                          |
| Maximum frame size                        | 16 MB                                                               |
| Concurrent sessions                       | The executing OpenAI account's Live limit                           |

At a limit, Gateway asks OpenAI to finalize (so you still get `session.closed` with final usage), then closes with `4408`. A refused handshake (invalid key, exhausted budget, rate limit) returns an HTTP status; a refusal after the socket opens sends an `error` event, then closes with `4000` plus the HTTP status:

| Close code             | Cause                                                                                                                                                                                                                                                  |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `4400`                 | Not a Live model (`unsupported_endpoint_for_model`), an `@alias/...` model (`alias_not_supported`), `session.store` true under zero data retention (`store_not_allowed`), no `session.start` in time (`session_start_timeout`), or another bad request |
| `4401`, `4402`, `4403` | Unauthorized, out of budget, or a model blocked for your organization or key (`model_not_allowed`)                                                                                                                                                     |
| `4404`                 | Unknown model                                                                                                                                                                                                                                          |
| `4408`                 | Session limit reached                                                                                                                                                                                                                                  |
| `4502`                 | Upstream connection failed (`upstream_unavailable`)                                                                                                                                                                                                    |

WebRTC, SIP telephony, sideband connections, session forking, and recording download aren't available through Gateway.

## Next steps

#### [Speech to text](/merge-gateway/capabilities/multimodal/speech-to-text)

Transcribe recorded audio

#### [Text to speech](/merge-gateway/capabilities/multimodal/text-to-speech)

Synthesize audio from text