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

# GPT Live 1

> Quick start, session parameters, messages, billing, and limits for openai/gpt-live-1: continuous real-time voice sessions over WebSocket, billed by session seconds.

GPT Live 1 is OpenAI's continuous voice model. You keep one audio stream open, the model listens and speaks on the same timeline, and billing follows session duration instead of tokens. Your application handles any backend work the model delegates.

> [card] WSS
>
> `wss://api.modellix.ai/api/v1/live/openai/gpt-live-1`

| Item | Value |
| - | - |
| Model ID | `openai/gpt-live-1` |
| Input | Raw 16-bit, little-endian, mono PCM audio at 24 kHz, plus text instructions and context |
| Output | Raw 16-bit, little-endian, mono PCM audio at 24 kHz, plus input and output transcripts |
| Delegation | `client` only |
| Auth | `Authorization: Bearer <Modellix API Key>` on the handshake |

For authentication, handshake errors, and close codes shared by all Live models, see the [Live Overview](https://docs.modellix.ai/live/overview.md).

## Quick Start

Prepare a short mono, 16-bit PCM WAV file at 24 kHz and set `MODELLIX_API_KEY`. Save the following code as `gpt_live.py` and run `python gpt_live.py question.wav`. The script streams the file followed by 5 seconds of silence so the model has time to answer, then closes the session and saves the reply as raw PCM in `reply.pcm`.

```python Python theme={null}
# pip install "websockets>=14"
import asyncio
import base64
import json
import os
import sys
import wave

import websockets

URL = "wss://api.modellix.ai/api/v1/live/openai/gpt-live-1"
CHUNK_FRAMES = 4800  # 200 ms at 24 kHz


async def append(ws, chunk):
    await ws.send(json.dumps({
        "type": "session.input_audio.append",
        "audio": base64.b64encode(chunk).decode("ascii"),
    }))
    await asyncio.sleep(len(chunk) / 48000)


async def send_audio(ws, path):
    with wave.open(path, "rb") as audio:
        if (audio.getnchannels(), audio.getsampwidth(), audio.getframerate()) != (1, 2, 24000):
            raise ValueError("Use mono, 16-bit PCM WAV audio at 24 kHz")
        while chunk := audio.readframes(CHUNK_FRAMES):
            await append(ws, chunk)
    # Keep the stream continuous while the model answers.
    for _ in range(25):
        await append(ws, bytes(CHUNK_FRAMES * 2))
    await ws.send(json.dumps({"type": "session.close"}))


async def receive_events(ws):
    with open("reply.pcm", "wb") as output:
        async for raw in ws:
            event = json.loads(raw)
            if event["type"] == "session.output_audio.delta":
                output.write(base64.b64decode(event["delta"]))
            elif event["type"] == "session.output_transcript.delta":
                print(event["delta"], end="", flush=True)
            elif event["type"] == "error":
                raise RuntimeError(event["error"])
            elif event["type"] == "session.closed":
                print("\nReason:", event["reason"], "Seconds:", event["usage"]["seconds"])
                return
    raise RuntimeError("Connection ended before session.closed")


async def main(path):
    headers = {"Authorization": f"Bearer {os.environ['MODELLIX_API_KEY']}"}
    async with websockets.connect(URL, additional_headers=headers, max_size=16 * 1024 * 1024) as ws:
        # 1. The first frame must be session.start.
        await ws.send(json.dumps({
            "type": "session.start",
            "session": {
                "instructions": "Answer briefly.",
                "audio": {"output": {"voice": "marin"}},
            },
        }))
        started = json.loads(await ws.recv())
        if started["type"] != "session.started":
            raise RuntimeError(started)

        # 2. Stream audio and read events at the same time.
        await asyncio.gather(send_audio(ws, path), receive_events(ws))


if len(sys.argv) != 2:
    raise SystemExit("Usage: python gpt_live.py question.wav")

asyncio.run(main(sys.argv[1]))
```

## Client Messages

### session.start

The first frame must be `session.start` and must arrive within 30 seconds. Wait for `session.started` before sending audio. The following fields belong inside `session`, which is required even when empty.

**model** `string`

Optional. The URL selects the model, and Modellix sets this field to the upstream model identifier.

**instructions** `string`

System instructions, up to 16,384 tokens. They cannot be replaced after startup; add more with `session.instructions.append`.

**input** `object[]`

Starting conversation messages, up to 128 messages and 8,192 combined tokens. Roles: `developer`, `user`, `assistant`. Developer and user messages use `input_text`; assistant messages use `text` or `output_text`.

**audio.output.voice** `string` (default: marin)

Output voice. It cannot change after startup. See the [OpenAI voice list](https://developers.openai.com/api/docs/guides/live-conversations) for voice names.

**delegation** `object | null`

Allowed: omit it, `null`, or `{"type": "client"}`. All three select client delegation. `{"type": "responses"}` closes the connection with `1008` and is not forwarded.

### Other Client Events

**session.input\_audio.append** `event`

Appends raw base64-encoded audio in the `audio` field, with no WAV or other container header. The decoded length must be even. Keep sending during silence so the stream stays continuous. Appends are not acknowledged.

**session.input\_audio.mute** `event`

Mutes input. The session continues, and the model can still speak. Pair it with `session.input_audio.unmute`.

**session.instructions.append** `event`

Adds trusted instructions. `content` holds up to 500 tokens, and `delegation_id` is required: use `null` for session-wide context, or a client delegation ID for that task.

**session.thinking.append** `event`

Adds context the model should know but not say immediately. Takes the same `content` and `delegation_id` fields.

**session.commentary.append** `event`

Adds information the model may say aloud, possibly paraphrased. Takes the same `content` and `delegation_id` fields.

**session.close** `event`

Starts a graceful close. Keep reading until `session.closed`.

## Server Messages

Server events are forwarded unchanged.

**session.started** `event`

The session is ready. `session` holds the startup configuration, including `id`.

**session.output\_audio.delta** `event`

A base64-encoded audio chunk in `delta`.

**session.input\_transcript.delta** `event`

A fragment of the caller's transcript in `delta`, with `start_ms` and `end_ms`. Fragments have no item ID or turn marker, so your application groups them for display.

**session.output\_transcript.delta** `event`

A fragment of the model's transcript. It has the same fields and grouping rules as the input transcript. User and model fragments can interleave.

**session.delegation.created** `event`

The model created a unit of delegated work. For client delegation, run the work in your application and report back with `delegation.id` as `delegation_id` on `session.commentary.append` or `session.thinking.append`.

**session.usage.updated** `event`

A cumulative snapshot, about once per minute. `usage.seconds` is the running total, not an increment, and `context_window.usage_ratio` reports context use.

**session.closed** `event`

The final event. `usage.seconds` is the final session duration. `reason` is one of `close_requested`, `expired`, `content`, `remote_hangup`, or `connection_lost`.

**error** `event`

An upstream error with `error.type`, `error.code`, `error.message`, `error.param`, and `error.client_event_id`. A startup error prevents `session.started`. A command error does not always close a session.

See the [OpenAI GPT-Live guide](https://developers.openai.com/api/docs/guides/live-conversations) for session design and the [delegation guide](https://developers.openai.com/api/docs/guides/live-delegation) for the client delegation workflow.

## Billing

GPT Live 1 is billed by voice session duration, using the upstream `usage.seconds`. Seconds are charged from the per-minute price without rounding up to a whole minute. Unit prices and invoices follow the console.

* **Silence and waiting count.** The upstream duration includes silence and waiting time, so close sessions you no longer use.
* **Multiple records per session.** Each increase in `session.usage.updated` and the final `session.closed` usage produce a request record for the seconds added since the previous one. Cumulative values are never summed.
* **Empty sessions.** A session that ends with no reported duration has no charge.
* **Missing final usage.** If the connection ends before `session.closed`, only usage already received is billed. Send `session.close` and read `session.closed` so the final seconds are recorded.
* **Your own backend work.** Models and tools your application calls while handling a delegation are outside this session and are billed by whichever service you call.

## Sessions and Limits

* Audio is fixed at 24 kHz PCM16 mono on Modellix.
* Selecting `{"type": "responses"}` in `session.start` or `session.update` closes the connection with `1008`. The frame is not forwarded.
* WebRTC, session forking, recording retrieval, sideband connections, and other upstream HTTP endpoints are not available through Modellix.
* When context use passes 90%, the upstream starts a replacement voice engine in the same session with your original instructions and up to 8,192 tokens of history.
* Modellix closes any connection at its maximum connection duration with code `4408`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.