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

# Conversa em tempo real

> Estabeleça uma conexão WebSocket persistente para interação de voz e texto bidirecional de baixa latência com um modelo de conversa

## Introdução

A conversa em tempo real estabelece uma conexão persistente via WebSocket (um protocolo que mantém uma conexão duradoura e bidirecional entre o cliente e o servidor), envia seu áudio ou texto ao modelo de conversa em tempo real e o modelo devolve texto e voz de forma incremental. É adequada para assistentes de voz, perguntas e respostas em tempo real, prática de fala e outros cenários que exigem interação de ida e volta.

Assim como a [transcrição em tempo real](/pt/api/realtime-transcription), ela funciona sobre WebSocket, mas os usos são diferentes:

| Dimensão                           | Transcrição em tempo real             | Conversa em tempo real (esta página)                                |
| ---------------------------------- | ------------------------------------- | ------------------------------------------------------------------- |
| Objetivo                           | Converter fala em texto               | Manter uma conversa de vários turnos em que o modelo gera respostas |
| Direção                            | Unidirecional: áudio entra, texto sai | Bidirecional: áudio ou texto entra, texto e voz saem                |
| Parâmetros de conexão              | `?intent=transcription&model=...`     | Apenas `?model=...` (sem `intent`)                                  |
| Detecção de atividade de voz (VAD) | Não suportada, deve ser `null`        | Suportada, detecção automática de turnos disponível                 |
| Casos de uso típicos               | Legendas de reunião, ditado ao vivo   | Assistentes de voz, interação falada em tempo real                  |

**Modelo disponível:**

* **gpt-realtime-2.1**: modelo de conversa por voz, suporta entrada de áudio e texto e produz respostas de texto e voz em tempo real.

<Warning>
  **Esta API é destinada à integração no lado do servidor; navegadores não podem se conectar diretamente.** Por motivos de segurança, o gateway valida e rejeita conexões que carregam um cabeçalho `Origin`, rejeita o subprotocolo `openai-insecure-api-key` e aceita a chave apenas pelo cabeçalho padrão `Authorization`. Um WebSocket iniciado pelo navegador anexa automaticamente um cabeçalho `Origin` e, portanto, é rejeitado. Se você precisar de conversa em tempo real no frontend, estabeleça a conexão com o gateway a partir do seu próprio servidor e encaminhe o áudio e os resultados entre o frontend e o servidor.
</Warning>

## Início rápido

### Endpoint de conexão

```text theme={null}
wss://aihubmix.com/v1/realtime?model=gpt-realtime-2.1
```

* `model=gpt-realtime-2.1`: **obrigatório**, o modelo é fixado no momento da conexão pelo parâmetro da URL e não pode mais ser alterado durante a sessão (veja as restrições abaixo).
* **Atenção à diferença em relação à transcrição**: o endpoint de conversa **não** leva `intent=transcription`.

### Autenticação

Envie a chave em um cabeçalho HTTP padrão durante o handshake:

```text theme={null}
Authorization: Bearer $AIHUBMIX_API_KEY
```

### Requisitos de formato de áudio

O áudio de entrada e de saída suporta atualmente apenas um formato. Converta seu áudio antes de enviar:

* **Codificação**: PCM16 (inteiro com sinal de 16 bits, little-endian)
* **Taxa de amostragem**: 24000 Hz
* **Canais**: mono

Ou seja, `audio/pcm@24000`. Declarar outro formato (como G.711/µ-law) na entrada ou na saída é rejeitado e encerra a sessão.

<Note>
  Diferente da transcrição, as sessões de conversa **suportam** detecção de atividade de voz (turn\_detection / VAD). Quando ativada, o modelo determina automaticamente o fim de uma fala e dispara uma resposta; quando desativada (valor `null`), você controla quando confirmar o áudio e quando solicitar uma resposta. Escolha conforme a necessidade.
</Note>

## Configuração da sessão (session.update)

Após a conexão ser estabelecida, o cliente pode enviar um quadro `session.update` para configurar parâmetros da conversa (voz, instruções do sistema, se o VAD está ativo). O modelo da sessão de conversa já está ancorado pela URL de conexão, portanto **você pode conversar sem enviar `session.update`**; envie quando precisar de uma voz ou instruções personalizadas.

```json theme={null}
{
  "type": "session.update",
  "session": {
    "type": "realtime",
    "instructions": "You are a helpful voice assistant. Keep answers concise.",
    "audio": {
      "input": {
        "format": { "type": "audio/pcm", "rate": 24000 },
        "turn_detection": { "type": "server_vad" }
      },
      "output": {
        "format": { "type": "audio/pcm", "rate": 24000 },
        "voice": "alloy"
      }
    }
  }
}
```

### Parâmetros de configuração

<ParamField body="session.type" type="string" required>
  Tipo de sessão, `realtime` para o cenário de conversa.
</ParamField>

<ParamField body="session.instructions" type="string">
  Instruções do sistema que definem o papel, o tom e as restrições de resposta do modelo.
</ParamField>

<ParamField body="session.output_modalities" type="string[]">
  Modalidades de saída, `["audio"]` ou `["text"]`: com `["audio"]` (padrão) o modelo gera voz e o texto da resposta chega pelo evento `response.output_audio_transcript.delta`; com `["text"]` ele gera apenas texto, entregue por `response.output_text.delta`. Qualquer outra combinação (por exemplo `["audio", "text"]`) é rejeitada e retorna um evento `error`.
</ParamField>

<ParamField body="session.audio.input.format" type="object" required>
  Formato do áudio de entrada, fixado em `{ "type": "audio/pcm", "rate": 24000 }`.
</ParamField>

<ParamField body="session.audio.input.turn_detection" type="object | null">
  Detecção de atividade de voz. Envie `{ "type": "server_vad" }` para ativar a detecção automática de turnos; envie `null` para desativá-la e confirmar o áudio e solicitar respostas manualmente pelo cliente.
</ParamField>

<ParamField body="session.audio.output.format" type="object" required>
  Formato do áudio de saída, fixado em `{ "type": "audio/pcm", "rate": 24000 }`.
</ParamField>

<ParamField body="session.audio.output.voice" type="string">
  Voz da resposta de áudio. **Ela não pode ser alterada depois do início da primeira resposta**: quando a sessão entra em estado de geração, um `voice` enviado novamente é ignorado (os demais ajustes continuam valendo). Portanto, se precisar de uma voz específica, defina-a antes de solicitar a primeira resposta.
</ParamField>

<Note>
  As capacidades a seguir **não são suportadas nesta versão** e fecham a sessão quando configuradas (código de fechamento `1008`): ativar a transcrição embutida dentro de uma sessão de conversa (`audio.input.transcription`, motivo `input_transcription_not_supported`), injetar áudio (motivo `item_audio_not_supported`) ou imagens (motivo `image_input_not_supported`) pelos itens de conversa, e qualquer tipo de item de conteúdo diferente de texto (motivo `unsupported_content_part`). Envie todo o áudio pelo canal `input_audio_buffer.append`.
</Note>

## Envio de entrada

### Envio de áudio

Divida o áudio PCM16 em pequenos fragmentos (por exemplo, um a cada 100 ms), codifique-os em base64 e envie-os continuamente com o evento `input_audio_buffer.append`:

```json theme={null}
{
  "type": "input_audio_buffer.append",
  "audio": "<fragmento de áudio PCM16 codificado em base64>"
}
```

Com o VAD ativado, o modelo detecta o fim de uma fala e dispara uma resposta automaticamente. Com o VAD desativado, após enviar um trecho de áudio você deve confirmar e solicitar uma resposta manualmente:

```json theme={null}
{ "type": "input_audio_buffer.commit" }
{ "type": "response.create" }
```

### Envio de texto

Você também pode injetar diretamente uma mensagem de texto e então solicitar uma resposta:

```json theme={null}
{
  "type": "conversation.item.create",
  "item": {
    "type": "message",
    "role": "user",
    "content": [{ "type": "input_text", "text": "Introduce yourself in one sentence." }]
  }
}
{ "type": "response.create" }
```

## Recebimento de respostas

O servidor continua enviando eventos. Tipos de evento principais:

<ParamField body="session.created / session.updated" type="event">
  Confirmação de que a sessão foi criada ou de que sua configuração foi atualizada. Você pode começar a enviar áudio e texto ao receber `session.created`.
</ParamField>

<ParamField body="conversation.item.added / conversation.item.done" type="event">
  Um item de conversa foi gravado: cada entrada do usuário e cada resposta do modelo adicionam um item.
</ParamField>

<ParamField body="input_audio_buffer.speech_started / input_audio_buffer.speech_stopped" type="event">
  Com a VAD ativada, o servidor detectou que o usuário começou ou parou de falar. Um evento `speech_started` geralmente significa que o usuário está interrompendo o modelo; consulte [Interrupção e truncamento](#interruption).
</ParamField>

<ParamField body="response.created" type="event">
  Uma resposta começou a ser gerada.
</ParamField>

<ParamField body="response.output_item.added / response.output_item.done" type="event">
  O item de saída da resposta começou e terminou. O campo `item.id` do evento `added` é o ID do item de conversa que você referencia ao truncar o áudio depois.
</ParamField>

<ParamField body="response.output_audio.delta / response.output_audio.done" type="event">
  Um trecho **incremental** do áudio da resposta (PCM16 codificado em base64) e sua marca de fim, que você pode reproduzir conforme chega.
</ParamField>

<ParamField body="response.output_audio_transcript.delta / response.output_audio_transcript.done" type="event">
  A transcrição **incremental** correspondente ao áudio da resposta, frase por frase, e sua marca de fim; o campo `delta` contém o texto adicionado. **Quando a saída inclui áudio, obtenha o texto da resposta neste evento**, por exemplo para exibir legendas enquanto o áudio é reproduzido.
</ParamField>

<ParamField body="response.output_text.delta / response.output_text.done" type="event">
  Um trecho **incremental** de uma resposta somente texto e sua marca de fim, emitidos apenas quando a modalidade de saída é somente texto (`output_modalities: ["text"]`).
</ParamField>

<ParamField body="response.done" type="event">
  Uma resposta terminou. Este evento carrega o uso de tokens do turno (`usage`), que é a base da cobrança.
</ParamField>

<ParamField body="conversation.item.truncated" type="event">
  Confirmação de que a solicitação de truncamento teve efeito; consulte [Interrupção e truncamento](#interruption).
</ParamField>

<ParamField body="error" type="event">
  Evento de erro, com código e descrição. Um problema da própria solicitação (por exemplo um valor `output_modalities` inválido) retorna um único evento `error` e a sessão continua utilizável; questões de política (troca de modelo, saldo esgotado) fecham a sessão.
</ParamField>

<Note>
  **Escolha o evento certo para o texto da resposta.** Por padrão (saída com áudio) o modelo envia apenas `response.output_audio_transcript.delta` e **não** envia `response.output_text.delta`; defina a modalidade de saída como somente texto e o texto passa para `response.output_text.delta`. Escute os dois em qualquer um dos modos para não perder texto (veja a [saída medida](#run-output) abaixo).
</Note>

<h2 id="interruption">
  Interrupção e truncamento
</h2>

Quando o usuário começa a falar enquanto o modelo fala, o conteúdo já gerado mas ainda não reproduzido entra em conflito com a próxima frase do usuário. Em uma conexão WebSocket a reprodução é responsabilidade do cliente, então o cliente também conclui a limpeza após uma interrupção.

Com a VAD ativada, o servidor envia `input_audio_buffer.speech_started` assim que detecta que o usuário começou a falar. Ao receber esse evento, o cliente deve:

1. **Parar imediatamente a reprodução local** e registrar até onde a resposta já havia sido reproduzida (em milissegundos).
2. Enviar `conversation.item.truncate` para remover da conversa o áudio não reproduzido, para que o modelo não o trate como dito no turno seguinte.

```json theme={null}
{
  "type": "conversation.item.truncate",
  "item_id": "item_ABC123",
  "content_index": 0,
  "audio_end_ms": 1500
}
```

* `item_id`: o ID do item de conversa desta resposta, obtido de `item.id` no evento `response.output_item.added`.
* `content_index`: o índice da parte de conteúdo de áudio, sempre `0`.
* `audio_end_ms`: o comprimento de áudio a manter, em milissegundos, conforme a posição realmente reproduzida pelo cliente.

O servidor responde com `conversation.item.truncated` quando a solicitação é processada. O truncamento afeta apenas o áudio desta resposta e a transcrição correspondente; a sessão não é afetada e você pode seguir para o próximo turno. Com o SDK da OpenAI, chame `conn.conversation.item.truncate(item_id=..., content_index=0, audio_end_ms=...)`.

Com a VAD desativada (por exemplo, apertar para falar), o toque no botão é a interrupção: envie `response.cancel` para cancelar a resposta em andamento e trunque conforme descrito acima; ao soltar, envie `input_audio_buffer.append`, `input_audio_buffer.commit` e `response.create` nessa ordem.

## Exemplos completos

Abaixo são mostradas três abordagens; escolha uma:

* **SDK oficial da OpenAI (recomendado)**: sem precisar escrever os quadros WebSocket à mão; aponte `websocket_base_url` (o parâmetro de URL base de WebSocket do SDK) para o gateway e reutilize a biblioteca oficial.
* **SDK oficial da OpenAI Agents**: a forma de voz em tempo real do framework de agentes oficial; troque a `url` de `model_config` pelo endereço do gateway.
* **websockets puro**: sem SDK, troque quadros diretamente conforme o protocolo. Menos dependências e mais fácil de depurar.

<Note>
  **Por que passar o nome do modelo na conexão?** O gateway da AiHubMix precisa do `model` no momento do handshake WebSocket para selecionar o provedor de modelos, autenticar e reservar cota, enquanto o `session.update` chega apenas depois que o handshake termina. Por isso, com um SDK você deve passar `model` explicitamente para `connect()` (o SDK o insere na consulta da URL); sem ele, o gateway **rejeita durante o handshake** e nenhuma conexão é estabelecida. Diferente da transcrição, o endpoint de conversa **não** precisa de `intent=transcription`.
</Note>

<CodeGroup>
  ```python Python (SDK da OpenAI) theme={null}
  # Dependência: pip install "openai[realtime]"
  import asyncio
  import base64
  from openai import AsyncOpenAI

  # Reutilizar o SDK oficial exige apenas duas adaptações:
  #   1) apontar websocket_base_url para o gateway da AiHubMix (em vez do endereço padrão da OpenAI)
  #   2) passar model explicitamente para connect(), necessário ao handshake (a conversa não precisa de intent)
  client = AsyncOpenAI(
      api_key="sk-***",  # Substitua pela sua chave de API da AiHubMix
      websocket_base_url="wss://aihubmix.com/v1",
  )

  async def main():
      async with client.realtime.connect(model="gpt-realtime-2.1") as conn:
          # 1) Configurar a sessão: instruções do sistema + voz + VAD (detecção automática de turnos)
          await conn.session.update(session={
              "type": "realtime",
              "instructions": "You are a helpful voice assistant. Keep answers concise.",
              "audio": {
                  "input": {
                      "format": {"type": "audio/pcm", "rate": 24000},
                      "turn_detection": {"type": "server_vad"},
                  },
                  "output": {
                      "format": {"type": "audio/pcm", "rate": 24000},
                      "voice": "alloy",
                  },
              },
          })

          # 2) Enviar uma mensagem de texto e solicitar uma resposta (para áudio, veja append/commit no exemplo puro)
          await conn.conversation.item.create(item={
              "type": "message",
              "role": "user",
              "content": [{"type": "input_text", "text": "What is the capital of France?"}],
          })
          await conn.response.create()

          # 3) Receber a resposta deste turno: o texto chega como deltas de transcrição e a voz como deltas de áudio
          async for event in conn:
              if event.type == "response.output_audio_transcript.delta":
                  print(event.delta, end="", flush=True)  # texto da resposta (saída com áudio)
              elif event.type == "response.output_audio_transcript.done":
                  print()  # transcrição deste turno concluída
              elif event.type == "response.output_text.delta":
                  print(event.delta, end="", flush=True)  # saída somente de texto
              elif event.type == "response.output_text.done":
                  print()
              elif event.type == "response.output_audio.delta":
                  pass  # fragmento de áudio PCM16 em base64, decodifique para reproduzir
              elif event.type == "input_audio_buffer.speech_started":
                  pass  # interrupção do usuário: parar a reprodução e chamar conn.conversation.item.truncate(...)
              elif event.type == "response.done":
                  print("\n[turno concluído]", event.response.usage)
                  break
              elif event.type == "error":
                  print("\n[erro]", event.to_dict())
                  break

  asyncio.run(main())
  ```

  ```python Python (websockets) theme={null}
  import asyncio
  import base64
  import json
  import websockets

  API_KEY = "sk-***"  # Substitua pela sua chave de API da AiHubMix
  URL = "wss://aihubmix.com/v1/realtime?model=gpt-realtime-2.1"

  async def main():
      # websockets >= 13 usa additional_headers; versões mais antigas usam extra_headers
      async with websockets.connect(
          URL, additional_headers={"Authorization": f"Bearer {API_KEY}"}
      ) as ws:
          # 1) Configurar a sessão (VAD desativado, controle manual do momento da resposta)
          await ws.send(json.dumps({
              "type": "session.update",
              "session": {
                  "type": "realtime",
                  "instructions": "You are a helpful voice assistant.",
                  "audio": {
                      "input": {
                          "format": {"type": "audio/pcm", "rate": 24000},
                          "turn_detection": None,
                      },
                      "output": {
                          "format": {"type": "audio/pcm", "rate": 24000},
                          "voice": "alloy",
                      },
                  },
              },
          }))

          # 2) Ler um arquivo PCM16 / 24 kHz / mono bruto local, enviá-lo em fragmentos e então commit + solicitação
          async def send_audio():
              with open("audio_pcm16_24k.raw", "rb") as f:
                  pcm = f.read()
              chunk = 24000 * 2 // 10  # 100 ms = taxa de amostragem x 2 bytes / 10
              for i in range(0, len(pcm), chunk):
                  await ws.send(json.dumps({
                      "type": "input_audio_buffer.append",
                      "audio": base64.b64encode(pcm[i:i + chunk]).decode(),
                  }))
                  await asyncio.sleep(0.1)  # simular o ritmo em tempo real
              # Com o VAD desativado, confirmar e solicitar uma resposta manualmente
              await ws.send(json.dumps({"type": "input_audio_buffer.commit"}))
              await ws.send(json.dumps({"type": "response.create"}))

          asyncio.create_task(send_audio())

          # 3) Receber a resposta
          async for msg in ws:
              evt = json.loads(msg)
              etype = evt.get("type", "")
              if etype == "response.output_audio_transcript.delta":
                  print(evt.get("delta", ""), end="", flush=True)  # texto da resposta (saída com áudio)
              elif etype == "response.output_audio_transcript.done":
                  print()  # transcrição deste turno concluída
              elif etype == "response.output_text.delta":
                  print(evt.get("delta", ""), end="", flush=True)  # saída somente de texto
              elif etype == "input_audio_buffer.speech_started":
                  pass  # interrupção do usuário: parar a reprodução e enviar conversation.item.truncate
              elif etype == "response.output_audio.delta":
                  pass  # fragmento de áudio PCM16 em base64, decodifique para reproduzir
              elif etype == "response.done":
                  print("\n[turno concluído]", evt.get("response", {}).get("usage"))
                  break
              elif etype == "error":
                  print("\n[erro]", evt.get("error"))
                  break

  asyncio.run(main())
  ```

  ```python Python (SDK da Agents) theme={null}
  # Dependência: pip install openai-agents
  import asyncio
  from agents.realtime import (
      OpenAIRealtimeWebSocketModel,
      RealtimeAgent,
      RealtimeSession,
  )

  agent = RealtimeAgent(
      name="Assistant",
      instructions="You are a helpful voice assistant. Keep answers concise.",
  )

  async def main():
      session = RealtimeSession(
          OpenAIRealtimeWebSocketModel(),
          agent,
          None,
          model_config={
              "api_key": "sk-***",  # Substitua pela sua chave de API da AiHubMix
              "url": "wss://aihubmix.com/v1/realtime?model=gpt-realtime-2.1",
          },
          run_config={
              "model_settings": {
                  "modalities": ["audio"],
                  "output_audio_format": "pcm16",
                  # Obrigatório desativar: a transcrição do áudio de entrada ativada por padrão
                  # não é suportada por esta API, caso contrário a sessão é encerrada com 1008
                  "input_audio_transcription": None,
              },
          },
      )
      async with session:
          await session.send_message("What is the capital of France?")
          async for event in session:
              if getattr(event, "type", "") == "raw_model_event":
                  data = getattr(event, "data", None)
                  if getattr(data, "type", "") == "transcript_delta":
                      print(getattr(data, "delta", ""), end="", flush=True)
                  elif getattr(data, "type", "") == "turn_ended":
                      print("\n[turno concluído]")
                      return

  asyncio.run(main())
  ```

  ```bash Teste de conexão (wscat) theme={null}
  # Verifique rapidamente a conectividade e a autenticação com wscat (requer npm i -g wscat)
  wscat -c "wss://aihubmix.com/v1/realtime?model=gpt-realtime-2.1" \
    -H "Authorization: Bearer $AIHUBMIX_API_KEY"

  # Após conectar, cole um quadro session.update e envie uma mensagem de texto + response.create para começar
  ```
</CodeGroup>

<Tip>
  Para converter qualquer áudio no formato PCM bruto exigido por esta API, use o ffmpeg:

  ```bash theme={null}
  ffmpeg -i input.mp3 -f s16le -acodec pcm_s16le -ac 1 -ar 24000 audio_pcm16_24k.raw
  ```
</Tip>

### Reutilizar exemplos oficiais

A maioria dos exemplos de conversa em tempo real publicados pela OpenAI depende apenas do parâmetro de URL base do SDK, então você pode reutilizá-los apontando o endereço para o endpoint da AiHubMix:

| Exemplo oficial                                                                                | Reutilizável apenas trocando o endereço | O que precisa ser alterado                                                                                                                                                                                                                         |
| ---------------------------------------------------------------------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Exemplo de conversa em tempo real do SDK oficial de Python                                     | Sim                                     | Definir `websocket_base_url` como `wss://aihubmix.com/v1` e passar `model` para `connect()`                                                                                                                                                        |
| Exemplo de conversa em tempo real do SDK oficial de Node / JS                                  | Sim                                     | Definir `baseURL` como `https://aihubmix.com/v1` (o SDK converte para `wss` e monta `/realtime?model=...`)                                                                                                                                         |
| Exemplo de voz em tempo real do SDK oficial de Agents                                          | Sim                                     | Definir `model_config.url` como `wss://aihubmix.com/v1/realtime?model=gpt-realtime-2.1` e desativar a transcrição do áudio de entrada que ele ativa por padrão                                                                                     |
| Exemplos oficiais para navegador (como o console em tempo real e o exemplo de voz multiagente) | Não                                     | Esses exemplos se conectam diretamente do navegador e são rejeitados pela validação de `Origin` do gateway; além disso, dependem de credenciais temporárias emitidas por um servidor, enquanto esta API expõe apenas WebSocket no lado do servidor |

<h2 id="run-output">
  Saída medida (em produção)
</h2>

A seguir está o resultado real do exemplo do SDK da OpenAI no ambiente de produção `aihubmix.com` com o modelo `gpt-realtime-2.1`. A sessão ativa `server_vad`, e tanto os prompts quanto o áudio estão em inglês.

**Entrada de texto**

```text theme={null}
# Enviar o texto "What is the capital of France?"
[turno 1, texto] concluído em 2,33 s | saída de áudio de 2,70 s em PCM16
  resposta: The capital of France is Paris.
  usage: input_tokens 29 (text 29) / output_tokens 81 (audio 54 + text 27, incluindo reasoning 8)
```

**Entrada de áudio**

```text theme={null}
# Enviar 6,3 s de fala em inglês em fragmentos; o modelo detecta o fim do turno e responde
[turno 2, áudio] concluído em 9,59 s | saída de áudio de 4,35 s em PCM16
  resposta: Everything sounds good on my side, and I'm ready to keep this conversation going.
  usage: input_tokens 117 (audio 67 + text 50) / output_tokens 129 (audio 87 + text 42, incluindo reasoning 11)
Eventos de VAD: input_audio_buffer.speech_started / input_audio_buffer.speech_stopped
```

**Diferença medida entre duas configurações**

| Configuração                             | Resultado medido                                                                                                                                                                        |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `turn_detection: {"type": "server_vad"}` | Após enviar o áudio você recebe `input_audio_buffer.speech_started` e `speech_stopped`, e o modelo detecta o fim do turno e começa a responder sem `commit` e `response.create` manuais |
| `output_modalities: ["text"]`            | O texto da resposta passa para `response.output_text.delta`, não há eventos de saída de áudio no turno e `audio_tokens` é 0 no usage                                                    |

<Note>
  Confirmação medida: por padrão (saída com áudio) o texto da resposta chega caractere por caractere apenas por `response.output_audio_transcript.delta`, e `response.output_text.delta` nunca aparece; o `response.done` carrega o `usage` do turno; o handshake leva de 2 a 4 segundos, o custo da reserva de cota ao estabelecer a sessão.
</Note>

## Cobrança

* **Cobrança por token**: uma sessão de conversa devolve o uso de tokens de cada turno (`usage`) com o evento `response.done`, e a cobrança se baseia nele. O uso é medido separadamente por componente, incluindo **entrada de áudio, saída de áudio, entrada de texto e saída de texto**. O preço unitário de cada componente segue o preço exibido em tempo real na [página de detalhes do modelo](https://aihubmix.com/model/gpt-realtime-2.1).
* **Liquidação conforme o uso**: esta é uma conexão de longa duração, e os valores são descontados em tempo real a cada turno durante a sessão, sem uma liquidação única no final. Ao estabelecer a sessão é feita primeiro uma **reserva de cota** de aproximadamente um minuto de uso (apenas uma verificação de admissão, não uma cobrança real), e a reserva restante é liberada ao final da sessão. **Portanto, o saldo disponível da sua conta precisa cobrir pelo menos cerca de um minuto de uso para que a sessão seja estabelecida.**
* Cada registro de cobrança de conversa em tempo real pode ser consultado item por item em [Uso e faturamento](https://aihubmix.com).

## Limites e restrições

1. **Duração da sessão**: uma conexão WebSocket dura no máximo **62 minutos**, após o que o servidor a encerra (código de fechamento `1000`, motivo `session_duration_limit`); divida em segmentos se precisar de mais tempo.
2. **Desconexão por inatividade**: quando nem o cliente nem o modelo têm atividade por **5 minutos**, o servidor fecha a sessão (código de fechamento `1008`, motivo `idle_timeout`). A atividade de qualquer um dos lados reinicia o cronômetro, então uma resposta longa que continua sendo transmitida não é interrompida.
3. **Saldo insuficiente**: **ao estabelecer a sessão**, se o saldo disponível não cobrir a reserva de aproximadamente um minuto, o handshake é rejeitado de imediato (HTTP 403) e nenhuma sessão é estabelecida; **durante a sessão**, se o saldo se esgotar, a conexão estabelecida é encerrada imediatamente.
4. **Somente no lado do servidor**: conexões diretas do navegador não são suportadas (o cabeçalho `Origin` é validado); integre a partir do seu servidor.
5. **Modelo fixado**: o modelo é fixado na URL de conexão, e alterá-lo com `session.update` durante a sessão é rejeitado e encerra a sessão.
6. **Formato fixado**: o áudio de entrada e de saída suporta apenas `audio/pcm@24000` mono; outros formatos são rejeitados.
7. **Voz fixada**: o `voice` não pode ser alterado depois do início da primeira resposta; defina-o antes de solicitar a primeira resposta.
8. **Não suportado**: a transcrição embutida dentro de uma sessão de conversa, a injeção de conteúdo de áudio ou imagem pelos itens de conversa e qualquer tipo de item de conteúdo diferente de texto.
9. **Uma resposta por vez**: uma sessão permite apenas uma resposta em andamento; enviar outro `response.create` antes de o turno atual terminar é rejeitado e fecha a sessão (código de fechamento `1008`, motivo `response_already_active`).

## Erros comuns

| Cenário                                                   | Código de encerramento / status            | Descrição                                                                                |
| --------------------------------------------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------- |
| Serviço não ativado                                       | HTTP 403 `realtime_disabled`               | A conversa em tempo real não está aberta para este ambiente                              |
| Cabeçalho `Origin` presente / conexão direta do navegador | Handshake rejeitado                        | Use uma conexão no lado do servidor                                                      |
| Saldo insuficiente ao conectar                            | HTTP 403 `insufficient_user_quota`         | O saldo não cobre a reserva de aproximadamente um minuto; recarregue e tente novamente   |
| Alterar o modelo durante a sessão                         | `1008` `model_override_forbidden`          | O modelo só pode ser indicado na URL de conexão                                          |
| Formato de áudio diferente de PCM                         | `1008` `audio_format_unsupported`          | Converta para `audio/pcm@24000`                                                          |
| Alterar a voz após a primeira resposta                    | Campo `voice` ignorado                     | Defina `voice` antes de solicitar a primeira resposta                                    |
| Ativar a transcrição embutida                             | `1008` `input_transcription_not_supported` | Não suportado em sessões de conversa nesta versão                                        |
| Injetar áudio pelos itens de conversa                     | `1008` `item_audio_not_supported`          | Envie o áudio por `input_audio_buffer.append`                                            |
| Injetar imagens pelos itens de conversa                   | `1008` `image_input_not_supported`         | Não suportado nesta versão                                                               |
| Item de conversa com tipo de conteúdo diferente de texto  | `1008` `unsupported_content_part`          | O conteúdo dos itens de conversa admite apenas texto                                     |
| Valor `output_modalities` inválido                        | evento `error` `invalid_value`             | Apenas `["audio"]` e `["text"]` são suportados; a sessão não é fechada                   |
| Duração máxima da sessão atingida                         | `1000` `session_duration_limit`            | O limite de 62 minutos foi atingido; reconecte para continuar                            |
| Ambos os lados inativos por 5 minutos                     | `1008` `idle_timeout`                      | A atividade de qualquer um dos lados reinicia o cronômetro                               |
| Nova solicitação com uma resposta em andamento            | `1008` `response_already_active`           | Aguarde `response.done` antes de enviar `response.create`                                |
| `event_id` duplicado                                      | `1008` `duplicate_event_id`                | O `event_id` de cada evento do cliente deve ser único na sessão                          |
| `response.conversation` com valor não padrão              | `1008` `conversation_mode_not_supported`   | Apenas o modo de conversa padrão é suportado                                             |
| Cliente envia evento exclusivo do servidor                | `1008` `client_forged_lifecycle_event`     | No namespace `response.*` o cliente só pode enviar `response.create` e `response.cancel` |
| Saldo esgotado durante a sessão                           | Conexão encerrada                          | Recarregue e reconecte                                                                   |

***

Última atualização: 2026-09-21
