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

# Agentes de voz

> Contexto antes do alô dentro de 150 ms, atestado de rede, transcrição em duas levas e transbordo.

O agente de voz tem menos tempo que qualquer outro agente. Quem liga ouve silêncio enquanto o prompt é montado, então o contexto precisa estar pronto antes da primeira palavra, e cada consulta durante a ligação precisa caber num turno falado. Este guia liga um agente de voz à Niadra do toque do telefone ao fim da chamada: contexto durante o toque, consultas ao histórico no tamanho da fala, os dados da chamada que provam quem está ligando, a transcrição em duas levas e o transbordo para um atendente.

Seguimos a Marina Souza. Às 14h02 ela reclamou no WhatsApp que o técnico não apareceu. Às 14h06 o agente de cobrança lançou um crédito de US\$ 40 na fatura 0823. Às 14h07 ela liga. O agente de voz atende sabendo das duas coisas.

## O que o agente de voz recebe

A view `voice` é a view de canal mais curta. Traz quem está ligando, o que continua em aberto, o que outros agentes acabaram de fazer e os destaques do histórico que mudam a conversa, no tamanho certo para o agente ler antes de falar. O que aconteceu em outro canal depois da compilação chega em `live` e vai no fim do prompt.

| Orçamento                                    | Valor  | Onde se ajusta                                |
| -------------------------------------------- | ------ | --------------------------------------------- |
| Tempo do `context()` na voz                  | 150 ms | SDK `Timeouts.context_voice` / `contextVoice` |
| Tempo da navegação do histórico na voz       | 300 ms | SDK `navigation_voice` / `navigationVoice`    |
| Tokens devolvidos por uma busca no histórico | 300    | `max_tokens=300`                              |

O SDK tem o próprio tempo máximo, independente da plataforma de voz. Se o tempo acaba, a ligação segue e o agente fala com o que tem.

## Passo a passo

### 1. Leia o contexto enquanto o telefone toca

Abra a conversa assim que a plataforma de voz avisar da chamada, e peça o contexto no mesmo tratador. Quando a ligação é atendida, o contexto já está no prompt. O número de quem liga é o sujeito; o id da chamada na plataforma é o seu `conversation_id`.

<CodeGroup>
  ```python Python theme={null}
  from niadra import Niadra, phone

  niadra = Niadra(channel="voice")

  def on_incoming_call(call):
      conversation = niadra.conversation(
          "call-4471",
          subject=phone(call.from_number),  # +14155550123
          view="voice",
          verification="V1",
      )
      ctx = conversation.context()
      call.agent.set_instructions(f"{AGENT_INSTRUCTIONS}\n\n{ctx.system_block}")
      return conversation
  ```

  ```typescript TypeScript theme={null}
  import { Niadra, handles } from "@niadra/sdk";

  const niadra = new Niadra();

  async function onIncomingCall(call: IncomingCall) {
    const conversation = niadra.conversation({
      subject: handles.phone(call.fromNumber), // +14155550123
      channel: "voice",
      conversation_id: "call-4471",
      verification: "V1",
    });
    const ctx = await conversation.context();
    await call.agent.setInstructions(`${AGENT_INSTRUCTIONS}\n\n${ctx.text}`);
    return conversation;
  }
  ```

  ```bash cURL theme={null}
  curl -X POST "https://acme-prod.us-east-1.api.niadra.com/v1/context" \
    -H "Authorization: Bearer $NIADRA_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "subject": { "type": "phone_e164", "value": "+14155550123" },
      "view": "voice",
      "verification": "V1",
      "conversation_id": "call-4471"
    }'
  ```
</CodeGroup>

Com `conversation_id`, o servidor fixa o contexto: todo turno desta ligação recebe os mesmos bytes, e o provedor do modelo mantém o início do prompt em cache. O que muda durante a ligação (uma ação nova de outro agente, uma mensagem no WhatsApp) chega como delta e como turnos ao vivo. Em Python, acrescente `ctx.turn_block` no fim de cada turno; em TypeScript, acrescente `ctx.suffix`.

<Tip>
  Em TypeScript, a conversa no canal `voice` usa a view `voice` por padrão. Passe `target` com o provedor e o modelo do seu agente de voz quando souber, para o contexto sair no tamanho certo para o cache daquele modelo.
</Tip>

### 2. Envie os dados da chamada

O nível de verificação é o que a ligação provou, nunca um palpite. O atestado de rede da operadora (STIR/SHAKEN e equivalentes) é o sinal mais forte que uma chamada traz antes de qualquer pergunta. Nível A vira V2; níveis B e C viram V1. Número oculto ou de PABX fica em V0 ou V1, e o agente identifica a pessoa na conversa.

Mande os dados da chamada no bloco `voice` do primeiro evento. Uma ligação costuma ter dois ids, o da plataforma e o do tronco: coloque o segundo em `conversation_aliases`, para os dois apontarem para a mesma conversa.

<CodeGroup>
  ```python Python theme={null}
  conversation.customer(
      "Hi, it's Marina. The technician never showed up this morning.",
      conversation_aliases=["trunk-7f2a9"],
      voice={
          "ani": "+14155550123",
          "dnis": "+18005550100",
          "trunk": "sip-east-2",
          "network_attestation": "A",
          "answered_at": "2026-09-22T17:07:03Z",
          "turn_offset_ms": 2400,
      },
  )
  ```

  ```typescript TypeScript theme={null}
  conversation.track({
    speaker: "customer",
    text: "Hi, it's Marina. The technician never showed up this morning.",
    conversation_aliases: ["trunk-7f2a9"],
    voice: {
      ani: "+14155550123",
      dnis: "+18005550100",
      trunk: "sip-east-2",
      network_attestation: "A",
      answered_at: "2026-09-22T17:07:03Z",
      turn_offset_ms: 2400,
    },
  });
  ```
</CodeGroup>

O nível efetivo é sempre o menor de três valores: o que você pediu, o teto da sua fonte e o que a conversa provou. Uma fonte de agente automatizado tem teto V2. A resposta diz o que aconteceu em `verification.requested`, `verification.effective` e `verification.reason`.

### 3. Deixe o agente consultar o histórico, no tamanho da fala

O contexto responde sozinho à pergunta mais comum: a seção "Do histórico" diz se aquilo já aconteceu e como foi resolvido. Quando a cliente fala de algo mais antigo, o agente busca. Amarre as ferramentas a quem está ligando no seu código, para o modelo escolher a consulta e nunca o cliente, e use o orçamento de voz.

<CodeGroup>
  ```python Python theme={null}
  kit = niadra.tools(phone("+14155550123"), conversation_id="call-4471", voice=True)

  # Entregue kit.definitions ao seu modelo de voz; encaminhe as chamadas de ferramenta para cá:
  output = kit.call("search_customer_history", {"query": "credit for missed technician visit"})
  ```

  ```typescript TypeScript theme={null}
  const kit = conversation.tools(); // amarrado a quem liga, orçamento de voz

  // Entregue kit.definitions ao seu modelo de voz; encaminhe as chamadas de ferramenta para cá:
  const output = await kit.call("search_customer_history", { query: "credit for missed technician visit" });
  ```
</CodeGroup>

Uma busca feita numa conversa de voz devolve até 300 tokens, cortados por valor, com o bloco `recurrence` quando a consulta bate com uma categoria: "segunda visita perdida em 12 meses; na anterior, crédito de US\$ 40". Trecho literal de transcrição nunca vai para audiência de voz.

### 4. Registre o que o agente fala

Capture os turnos do agente à medida que acontecem. Chame `mark_injected()` (Python) ou `markInjected()` (TypeScript) toda vez que o contexto entrar no prompt: os próximos turnos e ações do agente passam a levar um `context_stamp` com o ETag daquele contexto e o momento em que entrou, e é assim que a medição do [aproveitamento do contexto](/concepts/context-use) separa o contexto atrasado do contexto não usado. Se o seu agente usa o cliente da OpenAI, o `wrap()` dos dois SDKs injeta o contexto, carimba e registra as respostas por você.

<CodeGroup>
  ```python Python theme={null}
  ctx = conversation.context()
  conversation.mark_injected(ctx)  # the pack went into this prompt
  conversation.agent("Marina, I can see the $40 credit on your August bill was already applied at 2:06 pm.")
  ```

  ```typescript TypeScript theme={null}
  const ctx = await conversation.context();
  conversation.markInjected(ctx); // the pack went into this prompt
  conversation.agent("Marina, I can see the $40 credit on your August bill was already applied at 2:06 pm.");
  ```
</CodeGroup>

### 5. Transfira para um atendente, com passagem de caso

Quando a cliente pede uma pessoa, registre a transferência antes de transferir. Com `mode="warm"`, a mesa que recebe lê a view `brief`: uma passagem de caso curta, no tamanho de uma fala, que a sua plataforma pode sussurrar ao atendente. O atendente continua na ferramenta que a sua empresa já usa; o contexto chega a ela por API, webhook ou MCP.

<CodeGroup>
  ```python Python theme={null}
  conversation.handoff("human", target_source="src_service_desk", reason="asked for a person", mode="warm")

  brief = niadra.context(subject=phone("+14155550123"), view="brief", conversation_id="call-4471")
  ```

  ```typescript TypeScript theme={null}
  await conversation.handoff({ target: "human", target_source: "src_service_desk", reason: "asked for a person", mode: "warm" });

  const brief = await niadra.context({ subject: handles.phone("+14155550123"), view: "brief", conversation_id: "call-4471" });
  ```
</CodeGroup>

Transferência cujo destino não lê o contexto em 10 minutos aparece como transferência sem leitura na medição.

### 6. Encerre a ligação e depois mande a transcrição final

Encerre a conversa no momento em que a ligação cai. Em Python, sair do bloco `with` já faz isso; você também pode chamar `end()`. A sessão fecha na hora, sem esperar a inatividade, e a memória derivada fica pronta em menos de um minuto.

<CodeGroup>
  ```python Python theme={null}
  niadra.track({
      "channel": "voice",
      "conversation_id": "call-4471",
      "handles": [phone("+14155550123")],
      "speaker": {"role": "system"},
      "kind": "system_event",
      "canonical_type": "call.ended",
      "voice": {"ended_at": "2026-09-22T17:11:40Z", "end_reason": "caller_hangup", "recording_ref": "rec_88213"},
  })
  conversation.end()
  ```

  ```typescript TypeScript theme={null}
  conversation.track({
    speaker: "system",
    kind: "system_event",
    canonical_type: "call.ended",
    voice: { ended_at: "2026-09-22T17:11:40Z", end_reason: "caller_hangup", recording_ref: "rec_88213" },
  });
  await conversation.end();
  ```
</CodeGroup>

Plataformas de voz entregam em duas levas: a transcrição em tempo real durante a ligação e uma melhor depois dela. Mande a transcrição pós-chamada como eventos novos no mesmo `conversation_id`, com chaves de idempotência próprias. Dado tardio nunca é atualização: ele marca a conversa para uma extração nova, e o episódio ganha outra versão. Turnos com confiança de transcrição abaixo do limite não contam como pergunta nem como afirmação na medição.

<Note>
  O áudio nunca viaja dentro de um evento. Envie a gravação com `upload_media()` em Python ou `uploadMedia()` em TypeScript, passando quem ligou como `subject`, para que apagar o cliente apague também a gravação, e ponha o `media_ref` devolvido em `voice.recording_ref` ou em `content.media_ref`, com o SHA-256. Por HTTP, [`POST /v1/media/uploads`](/api/media-uploads) devolve a URL e os `upload_headers` exatos que vão no `PUT`.
</Note>

## Próximos passos

<CardGroup cols={2}>
  <Card title="O contexto e as views" href="/concepts/context">
    camadas, fixação, turnos ao vivo e delta.
  </Card>

  <Card title="Identidade e verificação" href="/concepts/identity">
    como V0 a V4 são provados e limitados.
  </Card>

  <Card title="Navegação do histórico" href="/concepts/history">
    busca, linha do tempo e abrir item, com orçamento.
  </Card>

  <Card title="Agentes de WhatsApp" href="/guides/whatsapp-agents">
    o outro lado da mensagem das 14h02.
  </Card>
</CardGroup>
