AGTCloud API Docs
GuidesAPI ReferenceIntegrations and resourcesUpdate and help
Realtime

GPT-Live Realtime Voice

Use the GPT-Live WebSocket API for full-duplex voice interaction and session events.

GPT-Live provides full-duplex voice interaction over a persistent WebSocket connection. Within one session, you can send input audio, receive audio and transcripts, update session settings, and maintain multi-turn context. The currently documented model is gpt-live-1; actual availability depends on the current account and model catalog.

WSS
wss://api.agtcloud.cn/v1/live/sessions

Authentication and connection

Server applications use a Bearer API key in the WebSocket handshake header. The model is not included in the query string. Instead, configure it in the first session.start event after connecting.

ItemValue
Connection URLwss://api.agtcloud.cn/v1/live/sessions
AuthorizationBearer your-api-key
Model locationsession.start.session.model
Input audioBase64-encoded raw PCM audio chunks

Store long-lived API keys only on trusted servers. This page covers only this WebSocket route; it does not cover WebRTC, SIP, sideband connections, session forking, or recording downloads.

Session flow

  1. Establish a WebSocket connection with a Bearer API key.
  2. Send session.start to configure the model, instructions, audio, and delegation method.
  3. After receiving session.started, begin sending audio or application commands.
  4. Continuously process input transcripts, output transcripts, output audio, and usage events.
  5. After stopping audio production, send session.close.
  6. Continue receiving events and use the final usage in session.closed to complete session cleanup.

The end of a speaking turn is not the end of the session. Keep the same connection for a multi-turn conversation and send session.close only when the application is ready to end the entire session.

Start parameters

FieldTypeDescription
typestringAlways session.start in the first frame
event_idstringOptional client event identifier used to correlate errors
session.modelstringRequired GPT-Live model ID
session.instructionsstringOptional voice-assistant instructions
session.audio.formatobjectAudio format, such as audio/pcm at 24 kHz
session.audio.output.voicestringOutput voice, such as marin; available values depend on the model
session.delegationobjectDelegation method, using client or responses

After the connection starts, use session.update to change instructions or other mutable session settings, and wait for session.updated to confirm the change. Do not send session.start again on the same connection, and do not use an update event to switch the top-level voice model.

Audio preparation and examples

The examples use headerless, mono, signed 16-bit little-endian PCM audio at 24 kHz. Each 100 ms chunk is 4,800 bytes and is Base64-encoded before being sent.

# Prepare the input audio
ffmpeg -i input.wav -f s16le -acodec pcm_s16le -ac 1 -ar 24000 audio.pcm

# Play the saved output audio
ffplay -f s16le -ar 24000 -ac 1 reply.pcm

The Node.js example requires ws. The Python example only checks session startup and shutdown and does not send audio.

npm install ws
python -m pip install websocket-client
WSSGPT-Live session examples
import fs from 'node:fs';
import WebSocket from 'ws';

const key = process.env.API_KEY;
const file = process.argv[2];
if (!key || !file) throw new Error('API_KEY and a PCM file path are required');

const pcm = fs.readFileSync(file);
if (!pcm.length || pcm.length % 2) {
  throw new Error('PCM must be non-empty and aligned to 16-bit samples');
}

const ws = new WebSocket('wss://api.agtcloud.cn/v1/live/sessions', {
  headers: { Authorization: 'Bearer ' + key },
  handshakeTimeout: 10000,
});
const audioOut = fs.createWriteStream('reply.pcm');
let started = false;
let closing = false;
let finalized = false;
let sender;
let drain;
let offset = 0;
let inputFinishedAt = 0;
let lastOutputAt = 0;

function send(event) {
  ws.send(JSON.stringify(event));
}

function closeSession(reason) {
  if (closing) return;
  closing = true;
  clearInterval(sender);
  console.error('Ending request:', reason);
  if (started && ws.readyState === WebSocket.OPEN) {
    send({ type: 'session.close' });
    drain = setTimeout(() => ws.terminate(), 15000);
  } else {
    ws.terminate();
  }
}

const limit = setTimeout(() => closeSession('60-second example limit'), 60000);

ws.on('open', () => send({
  type: 'session.start',
  event_id: 'start_001',
  session: {
    model: 'gpt-live-1',
    instructions: 'Respond briefly in English and wait for the user to speak first.',
    audio: {
      format: { type: 'audio/pcm', rate: 24000 },
      output: { voice: 'marin' },
    },
    delegation: { type: 'client' },
  },
}));

ws.on('message', (raw) => {
  let event;
  try {
    event = JSON.parse(raw.toString());
  } catch {
    closeSession('Invalid JSON');
    return;
  }

  if (event.type !== 'session.output_audio.delta') {
    console.log(raw.toString());
  }
  if (event.type === 'session.started' && !started) {
    started = true;
    sender = setInterval(() => {
      if (closing || ws.readyState !== WebSocket.OPEN) return;
      if (offset >= pcm.length) {
        inputFinishedAt ||= Date.now();
        if (
          lastOutputAt &&
          Date.now() - Math.max(lastOutputAt, inputFinishedAt) >= 3000
        ) {
          closeSession('Output finished');
          return;
        }
        send({
          type: 'session.input_audio.append',
          audio: Buffer.alloc(4800).toString('base64'),
        });
        return;
      }
      const chunk = pcm.subarray(offset, offset + 4800);
      offset += chunk.length;
      send({
        type: 'session.input_audio.append',
        audio: chunk.toString('base64'),
      });
    }, 100);
  }
  if (event.type === 'session.output_audio.delta') {
    lastOutputAt = Date.now();
    audioOut.write(Buffer.from(event.delta, 'base64'));
  }
  if (event.type === 'session.output_transcript.delta') {
    lastOutputAt = Date.now();
  }
  if (event.type === 'session.closed') {
    finalized = true;
    ws.close(1000);
  }
  if (event.type === 'error') closeSession('Service error');
});

ws.on('error', (error) => {
  console.error(error.message);
  closeSession('Connection error');
});

ws.on('close', (code, reason) => {
  clearTimeout(limit);
  clearTimeout(drain);
  clearInterval(sender);
  audioOut.end();
  console.error({ code, reason: reason.toString(), finalized });
  if (!finalized || code !== 1000) process.exitCode = 1;
});

process.on('SIGINT', () => closeSession('Stopped by user'));

Event reference

This API sends Base64 audio with session.input_audio.append. It does not use Muse binary audio frames and endStream, or the OpenAI Realtime input_audio_buffer.* events.

Client events · Session

session.start

Starts a GPT-Live session with model, instruction, audio, and delegation settings.

FieldTypeRequiredDescriptionExample
typestringYesAlways `session.start`.session.start
event_idstringNoClient event identifier.start_001
session.modelstringYesGPT-Live model ID.gpt-live-1
session.instructionsstringNoConversation instructions for the voice assistant.Respond briefly in English.
session.audio.format.typestringNoAudio format type.audio/pcm
session.audio.format.rateintegerNoAudio sample rate.24000
session.audio.output.voicestringNoOutput voice.marin
session.delegation.typestringYesDelegation method.client
JSON example
{
  "type": "session.start",
  "event_id": "start_001",
  "session": {
    "model": "gpt-live-1",
    "instructions": "Respond briefly in English.",
    "audio": {
      "format": {
        "type": "audio/pcm",
        "rate": 24000
      },
      "output": {
        "voice": "marin"
      }
    },
    "delegation": {
      "type": "client"
    }
  }
}

Usage and billing

session.usage.updated returns the current cumulative session usage, not the incremental usage for one turn. Do not add values from multiple stages. During a normal shutdown, use session.closed.usage.seconds as the final session duration.

Charges may include voice-session duration, delegated model usage, and tool calls. See the model catalog for current pricing and model availability.

Delegation methods

  • client: The application handles delegated tasks itself. Use this method when first validating a basic voice session.
  • responses: The server runs the configured Responses model and tools. The target model, tools, and permissions must be available on the current site.

With responses, delegated model and tool usage may incur separate charges. Do not treat MODEL_ID in the example as a model that is always available.

Error handling

SymptomWhat to check
Handshake returns 401Check the Bearer API key, model permission, and current site address
Path returns 404Confirm that the final connection path is /v1/live/sessions and that /v1 is not repeated
First-frame error or startup timeoutSend session.start immediately after connecting and wait for session.started before sending audio
No audio or distorted audioCheck PCM bit depth, byte order, sample rate, channel count, and removal of the WAV header
No session.closed eventSave the last cumulative usage, error event, and close reason; do not automatically replay the entire audio input
Delegated task rejectedCheck the Responses model, tools, permissions, and current site capabilities

References