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

# Chamadas

> Faça, atenda, entre e saia de chamadas do WhatsApp em navegadores e no código do servidor com os SDKs da Polymorfa.

Um número conectado pode fazer e receber chamadas de voz e vídeo do WhatsApp.
Você adiciona chamadas ao seu produto com os SDKs da Polymorfa:

* **No navegador**, `@polymorfa/browser` conecta microfones, câmeras e
  alto-falantes a uma chamada, e `@polymorfa/react` e `@polymorfa/elements`
  renderizam a interface da chamada.
* **No código do servidor**, `@polymorfa/sdk/calls` permite que a sua aplicação
  fale e ouça em uma chamada, por exemplo para executar um agente de voz.
* **No seu servidor**, `@polymorfa/sdk` faz, atende e encerra chamadas e
  gerencia as configurações de chamadas.

Navegadores e código do servidor podem entrar na mesma chamada. Cada
participante ouve os demais e recebe o vídeo deles. Os SDKs cuidam das conexões
de mídia, das reconexões e da renovação de tokens; a sua aplicação lida apenas
com chamadas e participantes.

Os pacotes de chamadas são distribuídos no repositório de SDKs da Polymorfa.
Consulte [SDK TypeScript](/pt-br/sdks/typescript/installation) para a instalação.

## Como as chamadas funcionam

* **A chamada.** Ela toca, é atendida ou recusada e termina. Encerrá-la
  desconecta todos.
* **Participantes.** Um participante é quem age na chamada. A Polymorfa o
  deriva da credencial:
  * Um token de cliente age como `client:<ephemeralId>`, usando o
    `ephemeralId` com que foi emitido. Tokens de cliente não podem indicar
    outro participante.
  * Uma chave de equipe ou um token de projeto age como `server:<participant>`. Informe
    o nome opcional `participant` (de 1 a 128 caracteres entre `A-Z`, `a-z`,
    `0-9`, `.`, `_`, `:`, `@` e `-`). O padrão é `server:default`.
* **Conexões.** Cada aba do navegador ou cliente de servidor que transporta o
  áudio e o vídeo de um participante é uma conexão. Um participante pode ter
  várias conexões. Sair fecha uma conexão e nunca encerra a chamada.

Toda chamada recebida toca até que um participante a atenda ou recuse, ou até
que a janela de toque termine. Nada atende uma chamada automaticamente. A sua
aplicação decide quem é convidado, quem atende e se outros participantes podem
entrar.

## Autentique clientes de navegador

Navegadores e aplicativos móveis usam um token de cliente. Emita o token no seu
servidor com `POST /platform/client-tokens` e envie-o ao cliente. Mantenha as
chaves de equipe e os tokens de projeto no seu servidor. Consulte
[Tokens de cliente](/pt-br/console/client-tokens).

Ative as ações de chamada de que o cliente precisa nas
[regras de cliente](/pt-br/console/client-tokens#regras-da-sessão) da sessão:

| Ação | Permite |
| - | - |
| `voip_place` | Fazer chamadas, convidar participantes e se conectar às chamadas que você faz. |
| `voip_answer` | Atender, entrar e recusar chamadas, e se conectar às chamadas que você atende ou em que você entra. |
| `voip_signal` | Receber eventos de chamada, manter as conexões ativas, sair e encerrar chamadas. |

O SDK de navegador precisa das três para fazer e atender chamadas. Um token de
cliente age apenas em chamadas da sessão à qual está vinculado, e essa sessão
ainda precisa pertencer ao projeto do token. O `ephemeralId` dele precisa usar
os caracteres de participante listados acima; caso contrário, as requisições
de chamada retornam `403`.

Tokens de cliente expiram. Passe ao SDK uma função que busca um token novo no
seu servidor; o SDK renova o token nas conexões abertas antes que ele expire.
Quando um token é revogado ou deixa de autorizar a sessão, o SDK informa um
erro `unauthorized` e pede um novo token à sua função. Ele faz o mesmo quando a
Polymorfa não consegue confirmar o token por 3 minutos porque as verificações
estão com limite de taxa ou temporariamente indisponíveis.

## Chamadas no navegador

```ts theme={null}
import { createBrowserCalls, createClientTokenProvider } from "@polymorfa/browser";
import { CallSurface, DialPad, PolymorfaProvider } from "@polymorfa/react";

const calls = createBrowserCalls({
  session: "support",
  getClientToken: createClientTokenProvider(),
});
await calls.connect();

<PolymorfaProvider>
  <CallSurface controller={calls.controller} exclusive={false} />
  <DialPad controller={calls.controller} />
</PolymorfaProvider>;
```

`createClientTokenProvider()` solicita tokens a `/api/polymorfa/token` na sua
própria origem. Implemente essa rota no seu servidor e retorne um token de
cliente para o usuário conectado.

`CallSurface` mostra as chamadas que estão tocando, o palco da chamada, o vídeo
dos participantes, a lista de participantes e os controles de microfone,
câmera, dispositivos, saída e encerramento da chamada. `IncomingCallCard`,
`CallStage`, `ParticipantVideoGrid`, `ParticipantList`, `CallControls` e
`DialPad` estão disponíveis individualmente. Em HTML puro, atribua o mesmo
controlador ao elemento `<pmfa-call>` de `@polymorfa/elements`.

Para construir (build) a sua própria interface, leia
`calls.controller.getSnapshot()` e chame `place`, `answer`, `join`, `reject`,
`leave`, `end`, `setMuted` e `switchDevice` no controlador. Várias chamadas
podem tocar ao mesmo tempo; o controlador as lista em `snapshot.invitations` e
nunca recusa uma chamada por você. O vídeo de cada participante remoto é um
stream separado em `controller.remoteVideos`, identificado pelo participante.

Chame `await calls.dispose()` quando a sua aplicação liberar a interface de
chamadas.

## Chamadas a partir do código do servidor

```ts theme={null}
import { CallsClient } from "@polymorfa/sdk/calls";

const client = new CallsClient({
  token: process.env.POLYMORFA_KEY!,
  session: "support",
  participant: "voice-agent",
});

client.on("incoming", async (call) => {
  await call.answer({ exclusive: true });
  call.audio.on("data", (pcm) => recognizer.push(pcm));
  call.audio.write(synth.speak("Hello, how can I help?"));
  call.on("ended", (reason) => console.log("ended:", reason));
});

await client.connect();
```

* `call.audio` transporta o áudio combinado da chamada como PCM mono de 16
  bits com sinal, na taxa `call.audio.sampleRate`, nos dois sentidos. Escreva o
  áudio em tempo real.
* `call.video` recebe o vídeo de cada um dos outros participantes como um
  stream separado de quadros H.264 e envia um stream H.264 seu.
  `call.video.sources` indica o participante por trás de cada stream. Envie um
  keyframe (uma unidade de acesso com uma fatia IDR, com SPS e PPS nela ou
  antes dela) quando o evento `keyframeRequest` disparar. Os outros
  participantes começam a receber o seu vídeo a partir do seu primeiro IDR.
* Quando a conexão cai, o cliente a reconecta e mantém a chamada.

`@polymorfa/sdk/calls` roda no Node.js 22 ou posterior, ou no Node.js 20 com
uma implementação de `WebSocket` passada nas opções.

## Atender, entrar ou recusar

Toda chamada recebida gera `call.received`. Os SDKs a expõem como uma chamada
que está tocando.

| Método do SDK | Efeito |
| - | - |
| `answer({ exclusive, video })` | Atende a chamada que está tocando e conecta a sua mídia. |
| `join({ video })` | Entra em uma chamada que outro participante atendeu, sem fazer o claim dela. |
| `reject()` | Recusa uma chamada que está tocando. Isso encerra a chamada para todos. |
| `leave()` | Desconecta este cliente. A chamada continua para os demais. |
| `end()` | Encerra a chamada para todos. |

Escolha `exclusive` em cada atendimento:

* `exclusive: true` faz o claim da chamada. Os outros participantes param de
  tocar, não podem atender nem se conectar, e as conexões abertas deles são
  fechadas. Use quando uma pessoa cuida da chamada, por exemplo quando o
  primeiro agente a atender fica com ela.
* `exclusive: false` (o padrão) mantém a chamada compartilhada. Os outros
  participantes continuam tocando e podem entrar.

Um atendimento posterior em uma chamada compartilhada entra nela. Repetir um
atendimento como o mesmo participante é seguro. Somente o participante que
atendeu pode fazer o claim de uma chamada compartilhada depois; um claim de
qualquer outro participante falha com `call_claimed`, e assim os participantes
que já entraram continuam conectados. Em uma chamada que você fez, o
participante que fez a chamada é o participante que atendeu.

Quando outro participante faz o claim de uma chamada, o SDK indica que o claim
foi feito em outro lugar. Pare de tocar e não recuse a chamada: recusar encerra
a chamada para o participante que atendeu.

Uma chamada que ninguém atende nem recusa dentro da janela de toque termina com
`call.missed` e o motivo `ring_timeout`. Depois de atender, conecte a mídia logo;
uma chamada sem mídia conectada termina com o motivo `setup_timeout`.

## Fazer uma chamada

No navegador, chame `calls.controller.place("+15550100", { video: true })`.
No código do servidor, chame `client.place("+15550100")`. No seu servidor:

```ts theme={null}
import { MessagingClient } from "@polymorfa/sdk";

const messaging = new MessagingClient({
  credential: { type: "apiKey", value: process.env.POLYMORFA_KEY! },
});

const placed = await messaging.voip.place({
  session: "support",
  to: "+15550100",
  video: false,
  exclusive: true,
  participant: "agent-7",
});
```

A resposta traz o `callId` enquanto o dispositivo da pessoa chamada toca.
`call.accepted` vem em seguida, quando a pessoa atende, e `call.ended` quando a
chamada termina. `to` é um número de telefone E.164 ou um ID de usuário. Um
token de cliente faz chamadas pela sessão à qual está vinculado; uma chave de
equipe ou um token de projeto precisa indicar `session`.

### Chamadas para pessoas que nunca conversaram com o número

O WhatsApp às vezes restringe um número a iniciar conversas e chamadas apenas
com pessoas com quem ele já conversou. Enquanto essa restrição está ativa, a
Polymorfa recusa uma chamada para qualquer outra pessoa com
`403 number_restricted` antes que algo chegue ao WhatsApp, e retorna
`Retry-After` quando o WhatsApp informou um horário de término. Chamadas para
pessoas com quem o número já conversou continuam funcionando, assim como as
chamadas recebidas. A mesma verificação vale para
[convites](#invite-more-people).

`session.restriction_updated` avisa quando a restrição começa, muda e termina,
para que você possa pausar a prospecção a frio e retomá-la automaticamente. Se
uma chamada chegar ao WhatsApp enquanto o número está restrito, ela termina com
`reason: "call_restricted"`.

Consulte [Boas práticas de chamadas](/pt-br/guides/calls/best-practices) para
saber como escolher para quem ligar e como aumentar o volume de um número novo.

Os inícios de chamadas realizadas compartilham um limite por minuto para cada
sessão de origem da sua equipe. Mudar o destino ou usar outra chave ou outro
token de cliente não reinicia esse limite. Uma chamada acima do limite falha
com `429` sem tocar no destino. Espere o próximo minuto antes de tentar
novamente.

O WhatsApp pode restringir um número de dispositivo vinculado cujas chamadas
recebem denúncias. O risco é maior em chamadas para pessoas que nunca
interagiram com o número. Consulte [Boas práticas de chamadas](/pt-br/guides/calls/best-practices)
antes de fazer chamadas em volume.

## Controle de chamadas pelo servidor

`messaging.voip` também atende, entra, recusa e encerra chamadas a partir do
seu servidor, e convida mais pessoas:

| Método | Requisição HTTP |
| - | - |
| `accept(callId, { exclusive, video, participant })` | `POST /messaging/voip/calls/{id}/accept` |
| `reject(callId)` | `POST /messaging/voip/calls/{id}/reject` |
| `leave(callId, { connectionId })` | `POST /messaging/voip/calls/{id}/leave` |
| `end(callId)` | `DELETE /messaging/voip/calls/{id}` |
| `addParticipant(callId, { to })` | `POST /messaging/voip/calls/{id}/participants` |

`accept` retorna `answered` (`true` quando esta requisição atendeu a chamada,
`false` quando ela entrou em uma chamada já atendida), `answeredBy` e
`exclusive`. Consulte a [referência da API](/api/messaging) para os campos de
requisição e resposta.

## Convidar mais pessoas

`addParticipant` faz tocar o telefone de outro usuário do WhatsApp para que ele
entre em uma chamada ao vivo, transformando uma chamada individual em uma
chamada em grupo. Os SDKs informam quando participantes entram, mudam de estado
e saem; os webhooks informam as mesmas mudanças como
`call.participant_joined`, `call.participant_state` e
`call.participant_left`. Os participantes usam o mesmo `id` de usuário da
Polymorfa em todos os lugares. Aliases conhecidos aparecem como `phoneNumber`,
`bsuid` ou `username`. Os campos `audioMuted` e `video` são reservados e
continuam `false` porque o WhatsApp não informa nenhum dos dois estados por
participante.

Para adicionar pessoas pela sua própria aplicação, dê um token de cliente a
cada pessoa e peça que ela [entre](#answer-join-or-decline) na chamada
compartilhada.

## Áudio, vídeo e configurações de chamadas

Cada participante recebe um único stream de áudio combinado. As pessoas no
WhatsApp ouvem todos os participantes que você conecta à chamada. O que os seus
próprios participantes ouvem depende do modo conferência.

### Modo conferência

Os seus participantes são todos que você conecta a uma chamada: pessoas em um
navegador, os seus servidores e bots e o seu PBX por meio de um
[tronco SIP](/pt-br/guides/calls/sip-trunks). O modo conferência decide se eles
ouvem uns aos outros.

| Modo conferência | Cada um dos seus participantes ouve | As pessoas no WhatsApp ouvem |
| - | - | - |
| Ativado (padrão) | As pessoas no WhatsApp e todos os outros participantes que você conectou | Todos os seus participantes |
| Desativado | Apenas as pessoas no WhatsApp | Todos os seus participantes |

Ninguém ouve a própria voz em nenhum dos modos. Um participante com mais de uma
conexão com a mesma chamada, por exemplo durante uma reconexão, não ouve a si
mesmo pela outra conexão.

Desative o modo conferência quando cada participante fala com o lado do
WhatsApp de forma independente, por exemplo um bot que transcreve a chamada ao
lado de um agente humano que não deve ouvir o bot. Altere o modo por sessão:

```ts theme={null}
await messaging.voip.updateCallSettings("support", { conferenceMode: false });
const settings = await messaging.voip.retrieveCallSettings("support");
```

Uma atualização altera apenas as configurações que você envia; as demais
mantêm os valores. `revision` aumenta a cada alteração. Para aplicar uma
alteração somente se ninguém tiver alterado as configurações desde a sua
leitura, envie a `revision` que você leu como `expectedRevision`; a atualização
então falha com `409 state_conflict` se elas tiverem mudado. Leia as
configurações de novo e tente novamente.

Uma sessão sem configurações salvas retorna `callsEnabled: true`,
`conferenceMode: true`, `inboundRoute: "clients"`, `sipTrunkId: null`,
`sipClaim: true`, `hostCloudApiCalls: false`, `revision: 0` e
`updatedAt: null`. Ler exige `sessions:read` e salvar exige
`sessions:manage`, com uma chave de equipe ou um token de projeto. Tokens de
cliente não podem ler nem alterar as configurações de chamadas. Uma
configuração salva vale para uma chamada na próxima vez que um participante a
atender ou se conectar a ela, inclusive em chamadas em andamento. Quando não é
possível ler a configuração, atender e conectar falham com `503` em vez de usar
um valor que você não escolheu. Você também pode alterá-la no Console, nos
detalhes do número. Consulte [Get call settings](/api-reference/platform/calls/get-call-settings)
e [Update call settings](/api-reference/platform/calls/update-call-settings).

### Desativar chamadas em um número

Defina `callsEnabled` como `false` para interromper as chamadas em uma sessão
sem desconectá-la:

```ts theme={null}
await messaging.voip.updateCallSettings("support", { callsEnabled: false });
```

Enquanto as chamadas estão desativadas:

* fazer uma chamada, atender ou entrar em uma, convidar um participante e
  conectar mídia falham com `403 calls_disabled`. Iniciar ou atender uma
  chamada pelo endpoint `/calls` da Graph API falha com um erro de permissão;
* as chamadas recebidas são recusadas. Você ainda recebe `call.received`,
  seguido de `call.rejected` e `call.ended`; e
* um [tronco SIP](/pt-br/guides/calls/sip-trunks) não pode fazer chamadas pela
  sessão.

As chamadas em andamento continuam: o participante que atendeu ou fez uma
chamada ainda pode reconectar a mídia dele a ela, e sair, encerrar e recusar
chamadas continuam funcionando. Outros participantes não podem entrar em uma
chamada em andamento. Defina `callsEnabled` de volta como `true` para permitir
chamadas novamente. Uma alteração chega às chamadas recebidas e aos troncos SIP
em cerca de um minuto.

A Polymorfa encaminha o vídeo de cada participante como um stream separado e
não os combina em uma grade. O lado do WhatsApp da chamada recebe um único
stream de vídeo: a câmera do primeiro participante que envia vídeo, enquanto
esse participante continuar conectado.

Cada chamada informa as suas `capabilities`: `video` e `invite`. Os componentes
do SDK ocultam os controles que uma chamada não suporta.

## Números da Cloud API

Um número conectado pela WhatsApp Business Cloud API continua atendendo as
chamadas recebidas pela sua integração com a Graph API até que você ative
`hostCloudApiCalls` nas configurações de chamadas dele:

```typescript theme={null}
await messaging.voip.updateCallSettings("support", { hostCloudApiCalls: true });
```

Com `hostCloudApiCalls: true`, os seus apps de chamadas recebem e atendem as
chamadas recebidas do número, e o endpoint `/calls` da Graph API se recusa a
atendê-las. Com `false` (o padrão), o Polymorfa Calls não hospeda chamadas
recebidas no número, e a sua integração com a Graph API as atende. Enquanto as
[chamadas estão desativadas](#turn-calling-off-for-a-number), a Polymorfa
recusa as chamadas recebidas do número, seja qual for essa configuração.
A configuração vale para chamadas que chegam depois da alteração. As chamadas
que você faz com os SDKs sempre usam o Polymorfa Calls.

As chamadas em um número da Cloud API funcionam da mesma forma nos SDKs, com
estes limites:

* As chamadas são individuais e somente de áudio. As `capabilities` delas
  informam `video: false` e `invite: false`. Fazer uma chamada de vídeo ou
  convidar um participante falha com `unsupported_for_connection`. Atender com
  vídeo atende apenas com áudio.
* Antes de você fazer uma chamada, o destinatário precisa conceder à sua
  empresa permissão para ligar para ele. Sem isso, fazer a chamada falha com
  `call_permission_required`.
* O WhatsApp permite de 30 a 60 segundos para atender uma chamada recebida da
  Cloud API. Atenda em até 30 segundos para evitar uma chamada perdida.

## Webhooks

| Evento | Quando |
| - | - |
| `call.received` | Uma chamada recebida chegou e está tocando. Traz `capabilities` e a `sessionConnection` do número. |
| `call.accepted` | A chamada foi atendida por um participante ou pela pessoa chamada. Traz `answeredBy`, `capabilities` e, em uma chamada com claim, `exclusive: true`. |
| `call.rejected` | Um participante recusou uma chamada recebida que estava tocando. |
| `call.missed` | Uma chamada recebida que estava tocando nunca foi atendida (`reason: "ring_timeout"`). |
| `call.ended` | A chamada terminou, com `reason`, `direction` e `durationSeconds`. Uma chamada cujo host de mídia foi perdido informa `reason: "pod_lost"` e `from: null`. Uma chamada que o WhatsApp recusou porque o número está restrito a contatos existentes informa `reason: "call_restricted"`. |
| `call.telemetry` | Medições de tempo e de qualidade da chamada finalizada. |
| `call.participant_joined` | Um participante entrou na lista de participantes da chamada. |
| `call.participant_state` | O estado de um participante na chamada mudou. |
| `call.participant_left` | Um participante saiu da lista de participantes da chamada, com um motivo opcional. |
| `call.connection_joined` | Um dos seus participantes se conectou à chamada. Traz o participante e o `id` da conexão. |
| `call.connection_left` | Uma conexão saiu, com `reason`: `left`, `replaced` ou `claimed`. [Troncos SIP](/pt-br/guides/calls/sip-trunks) informam mais motivos. |
| `usage.recorded` | O uso da chamada encerrada foi medido. |

## Histórico de chamadas

O [Console](/pt-br/console/calls) lista as suas chamadas com estado, direção e
duração, e mostra os participantes, as conexões e as medições de qualidade de
cada chamada, os diagnósticos que o seu app informou e os resultados das
entregas de webhook. Proprietários e administradores podem encerrar uma chamada
ao vivo ou remover uma conexão dela ali.

## Análises e registros de chamadas

Três endpoints da Platform API leem o seu histórico de chamadas. Eles aceitam
uma chave de equipe ou um token de projeto com `sessions:read`. Um
token de projeto lê apenas o próprio projeto; uma chave de equipe lê a
equipe inteira, a menos que você informe `projectId`. O resumo das chamadas do
Console usa o mesmo endpoint de estatísticas.

Uma chamada mantém o projeto original depois que o número dela é excluído.
Reutilizar o nome do número em outro projeto não transfere o histórico, as
estatísticas nem o uso dele. Registros cujo projeto original é desconhecido têm
`projectId: null` e aparecem apenas em leituras de toda a equipe.

| Endpoint | Retorna |
| - | - |
| [`GET /platform/calls/stats`](/api-reference/platform/calls/get-call-stats) | Volume, resultados, taxa de atendimento, tempo em conversa e um mapa de calor por hora da semana |
| [`GET /platform/calls`](/api-reference/platform/calls/list-call-records) | Registros detalhados de chamadas em JSON, no máximo 100 por página |
| [`GET /platform/calls/export`](/api-reference/platform/calls/export-call-records) | Registros detalhados de chamadas em CSV ou NDJSON, no máximo 1.000 por página |

Os três aceitam os mesmos filtros:

| Filtro | Valores |
| - | - |
| `since`, `until` | Intervalo do horário de início. `since` é inclusivo e `until` é exclusivo. |
| `projectId` | Um projeto. |
| `sessionId` | Um número, pelo nome da sessão. |
| `direction` | `inbound` ou `outbound`. |
| `upstream` | `linked_device` ou `cloud_api`: como o número se conecta ao WhatsApp. |
| `outcome` | `answered`, `missed`, `declined`, `failed` ou `in_progress`. |

Cada chamada tem um único resultado:

* `answered`: a mídia se conectou. Chamadas atendidas ainda em andamento contam
  aqui.
* `declined`: um participante ou a outra parte recusou, ou a linha estava
  ocupada.
* `missed`: ninguém atendeu antes de a chamada parar de tocar, ou quem ligou
  desligou primeiro.
* `failed`: a chamada terminou antes de se conectar por qualquer outro motivo,
  como uma falha de conexão ou de capacidade.
* `in_progress`: a chamada ainda está tocando.

### Estatísticas

```bash theme={null}
curl "https://api.polymorfa.com/platform/calls/stats?since=2026-09-01T00:00:00Z&until=2026-09-08T00:00:00Z&groupBy=day&timezone=America/Sao_Paulo" \
  -H "Authorization: Bearer $POLYMORFA_API_KEY"
```

Sem `since` e `until`, o intervalo são os últimos 7 dias. `groupBy` seleciona
os grupos:

* `day` (o padrão) e `hour` retornam um grupo por dia ou por hora em
  `timezone`, incluindo os vazios. Um intervalo com `day` cobre no máximo 366
  dias, e um intervalo com `hour`, no máximo 31 dias.
* `session` retorna um grupo por número, começando pelos que têm mais chamadas,
  até 500 números. `groupsTruncated` é `true` quando mais números tiveram
  chamadas.
* `outcome` retorna um grupo por resultado.

`timezone` aceita um nome IANA como `UTC`, `CET` ou `Europe/Lisbon`, em
maiúsculas ou minúsculas; o `timezone` da resposta é o nome canônico.
Deslocamentos UTC e strings POSIX como `UTC+3` são recusados. Os grupos por
hora são horas do relógio local: no dia em que um fuso muda o relógio, a hora
pulada não tem grupo e a hora repetida forma um único grupo. Cada grupo e os
`totals` informam `calls`, a contagem de cada resultado, `answerRate`,
`totalDurationSeconds` e `averageDurationSeconds`. `answerRate` é o número de
chamadas atendidas dividido pelo número de chamadas que não estão mais tocando.
As durações contam apenas o tempo conectado. `heatmap` sempre tem 168 células,
uma por dia da semana (`1` é segunda-feira) e hora, em `timezone`.

### Registros detalhados de chamadas

Cada registro tem `callId`, `projectId`, `sessionId`, `direction`, `upstream`,
`outcome`, `state`, `hasVideo`, `peerRef`, `startedAt`, `connectedAt`,
`endedAt`, `durationSeconds` e `endReason`. `peerRef` é o pseudônimo da outra
parte, específico da equipe: a mesma pessoa tem o mesmo `peerRef` em todas as
chamadas da sua equipe, mas ele não é um número de telefone e a Polymorfa não
consegue convertê-lo de volta em um.

`GET /platform/calls` retorna os registros do mais novo para o mais antigo.
Passe `page.nextCursor` como `cursor`, com os mesmos filtros, para ler a
próxima página.

`GET /platform/calls/export` retorna os mesmos registros em CSV (`format=csv`,
o padrão) ou em JSON delimitado por quebras de linha (`format=ndjson`), até
1.000 por requisição. Cada página CSV começa com uma linha de cabeçalho. Quando
mais registros correspondem aos filtros, o cabeçalho de resposta
`Polymorfa-Next-Cursor` contém o cursor da próxima página:

```bash theme={null}
cursor=""
while :; do
  curl -sS -D headers.txt "https://api.polymorfa.com/platform/calls/export?format=ndjson&since=2026-09-01T00:00:00Z${cursor:+&cursor=$cursor}" \
    -H "Authorization: Bearer $POLYMORFA_API_KEY" >> calls.ndjson
  cursor=$(grep -i '^polymorfa-next-cursor:' headers.txt | cut -d' ' -f2 | tr -d '\r')
  [ -n "$cursor" ] || break
done
```

Em CSV, um valor de texto que começa com `=`, `+`, `-`, `@`, uma tabulação ou um
retorno de carro recebe um apóstrofo como prefixo, para que planilhas não o
executem como fórmula.

Use os endpoints HTTP autenticados acima para análises, registros e
exportações de chamadas. O SDK TypeScript e a CLI não expõem essas operações de
análise.

O histórico de chamadas pode ficar atrasado em relação aos eventos ao vivo
durante uma falha temporária de registro. As escritas que falharam são tentadas
novamente por até 24 horas. Um evento atrasado não faz uma chamada aceita ou
finalizada voltar a tocar.

## Retenção de dados de chamadas

> [!NOTE]
>
> A exclusão começa em uma data anunciada com antecedência no
> [changelog](/changelog). Até lá, escolher um período registra a sua escolha e
> não exclui nada.

A sua equipe escolhe por quanto tempo a Polymorfa mantém os dados de chamadas
dela. Quando a exclusão começar, a Polymorfa excluirá os dados de chamadas mais
antigos que esse período. O período vale para:

* registros de chamadas: o histórico de chamadas que o Console e a API listam;
* eventos de chamadas: o histórico de participantes e conexões de cada chamada;
* os diagnósticos que os seus apps informam sobre as conexões deles nas
  chamadas.

A Polymorfa não grava o áudio nem o vídeo das chamadas e não armazena o número
de telefone nem o endereço do WhatsApp da outra parte.

Escolha uma política:

| Política | Período |
| - | - |
| `short` | 7 dias |
| `standard` | 30 dias |
| `extended` (padrão) | 90 dias |
| `compliance` | 365 dias |
| `custom` | De 1 a 2.555 dias, definido por você |

Depois que a exclusão estiver em funcionamento, a Polymorfa exclui os dados de
chamadas em até 24 horas depois que eles ficam mais antigos que o período. Um
acúmulo grande, como os dados de chamadas já armazenados quando a exclusão
começa ou quando você encurta o período, pode levar mais tempo para ser
excluído. Uma chamada é medida a partir do momento em que começou; uma chamada
que ainda está tocando ou em andamento é mantida até terminar. O período cobre
todos os projetos da equipe.

> [!WARNING]
>
> Depois que a exclusão estiver em funcionamento, um período mais curto também
> vale para os dados de chamadas já armazenados: os dados mais antigos que o
> novo período são excluídos em até 24 horas e não podem ser recuperados. Um
> período mais longo não restaura dados que já foram excluídos.

Leia a configuração com qualquer chave de equipe ou token de projeto que tenha
`sessions:read`:

```bash theme={null}
curl https://api.polymorfa.com/platform/call-retention \
  -H "Authorization: Bearer $POLYMORFA_TEAM_KEY"
```

```json theme={null}
{
  "success": true,
  "data": {
    "policy": "extended",
    "retentionDays": 90,
    "appliesTo": ["call_records", "call_events", "client_reports"],
    "revision": 0,
    "updatedAt": null
  }
}
```

Altere a configuração com uma chave de equipe que tenha `sessions:manage`.
Envie `retentionDays` apenas com a política `custom`:

```bash theme={null}
curl -X PUT https://api.polymorfa.com/platform/call-retention \
  -H "Authorization: Bearer $POLYMORFA_TEAM_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "policy": "custom", "retentionDays": 180, "expectedRevision": 0 }'
```

* Tokens de projeto podem ler a configuração, mas não alterá-la; uma alteração
  falha com `403`. Tokens de cliente não podem fazer nenhuma das duas coisas.
* Uma equipe sem configuração salva retorna `revision: 0` e `updatedAt: null`.
  `revision` aumenta a cada alteração. Envie a `revision` que você leu como
  `expectedRevision` para recusar a atualização com `409 state_conflict` se
  alguém tiver alterado a configuração depois da sua leitura.
* Uma política nomeada com um `retentionDays` diferente, ou `custom` sem um,
  falha com `400 invalid_parameter`.
* `appliesTo` lista os tipos de dados de chamadas que o período cobre. Quando a
  Polymorfa passar a armazenar um novo tipo de dado de chamada, ele será
  adicionado a essa lista e seguirá o mesmo período.

Proprietários e administradores da equipe também podem alterar o período no
Console, na [página Chamadas](/pt-br/console/calls#call-data-retention).
Consulte [Get call retention](/api-reference/platform/calls/get-call-retention)
e [Update call retention](/api-reference/platform/calls/update-call-retention).

## Informar diagnósticos de chamadas pelo seu app

O seu app pode enviar as medições de qualidade e os erros que mede em cada uma
das conexões dele. O [Console](/pt-br/console/calls#app-reported-diagnostics)
os mostra na página de detalhes da chamada, ao lado das medições do serviço de
mídia da Polymorfa. Os relatórios são opcionais; uma chamada sem eles funciona
da mesma forma.

Envie os relatórios com `POST /messaging/voip/calls/{id}/reports`. Os SDKs da
Polymorfa não os enviam por você; chame o endpoint pelo seu app. Use a
credencial que transporta a chamada: um token de cliente precisa da ação
`voip_signal`, e uma chave de equipe ou um token de projeto informa como
`server:<participant>`.

```bash theme={null}
curl -X POST "https://api.polymorfa.com/messaging/voip/calls/CALL_ID/reports" \
  -H "Authorization: Bearer $POLYMORFA_CLIENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "quality",
    "connectionId": "conn_0123456789",
    "client": { "sdk": "my-call-app", "version": "2.3.0", "platform": "browser" },
    "quality": {
      "rttMs": 84,
      "jitterMs": 6,
      "packetsLost": 12,
      "packetsReceived": 4810,
      "audioCodec": "audio/opus",
      "candidateType": "relay",
      "reconnects": 0
    }
  }'
```

Um relatório bem-sucedido retorna `202` com `{ "success": true }`.

| Campo | Regra |
| - | - |
| `kind` | `quality` ou `error`. |
| `connectionId` | O ID que o seu app escolheu para a conexão, como `call.connection_joined` o informa. De 8 a 64 caracteres entre `A-Z`, `a-z`, `0-9`, `_` e `-`. |
| `participant` | Apenas chaves de equipe e tokens de projeto; consulte [Como as chamadas funcionam](#how-calls-work). Tokens de cliente o omitem. |
| `client` | Opcional. `sdk` (de 1 a 32 caracteres entre `a-z`, `0-9`, `@`, `/`, `.`, `_`, `-`), `version` (`MAJOR.MINOR.PATCH` com um sufixo opcional, no máximo 32 caracteres) e `platform` (`browser`, `node` ou `other`). |
| `quality` | Obrigatório para `kind: "quality"`. Envie pelo menos uma medição e omita as que você não mediu. |
| `error.code` | Obrigatório para `kind: "error"`. Um dos códigos abaixo. |

As medições de qualidade são números inteiros ou nomes curtos:

| Medição | Significado | Intervalo |
| - | - | - |
| `rttMs` | Tempo de ida e volta em milissegundos. | 0 a 60000 |
| `jitterMs` | Jitter de recepção em milissegundos. | 0 a 60000 |
| `packetsLost` | Pacotes perdidos desde o início da conexão. | 0 a 2147483647 |
| `packetsReceived` | Pacotes recebidos desde o início da conexão. | 0 a 2147483647 |
| `audioCodec` | Codec de áudio negociado, como `audio/opus`. | De 1 a 32 caracteres entre `A-Z`, `a-z`, `0-9`, `/`, `.`, `-` |
| `videoCodec` | Codec de vídeo negociado, como `video/VP8`. | Igual a `audioCodec` |
| `candidateType` | Tipo de candidato ICE local em uso: `host`, `srflx`, `prflx` ou `relay` (por um relay TURN). | — |
| `reconnects` | Quantas vezes a conexão se reconectou até agora. | 0 a 1000 |

Códigos de erro:

| Código | Significado |
| - | - |
| `media_permission_denied` | O usuário ou o navegador negou o acesso ao microfone ou à câmera. |
| `device_not_found` | Nenhum microfone ou câmera foi encontrado. |
| `device_in_use` | Outro aplicativo estava usando o microfone ou a câmera. |
| `ice_failed` | Não foi possível estabelecer a conexão de mídia, ou ela foi perdida. |
| `negotiation_failed` | A negociação SDP falhou. |
| `media_timeout` | A mídia parou de chegar. |
| `reconnect_exhausted` | O app parou de tentar reconectar. |
| `token_refresh_failed` | O app não conseguiu obter um novo token de cliente. |
| `unsupported_browser` | O navegador não oferece suporte a chamadas. |
| `other` | Qualquer outro erro. |

Os relatórios aceitam apenas esses campos. A Polymorfa não aceita mensagens de
erro, nomes de dispositivos, endereços IP, SDP nem user agents em um relatório;
uma requisição que os contenha falha com `400`.

Limites:

* Cada conexão pode enviar um relatório de qualidade a cada 5 segundos e 20
  relatórios de erro por minuto. Os excedentes falham com `429`. Os relatórios
  também contam para a regra `rateLimit` de um token de cliente.
* A Polymorfa aceita relatórios enquanto a chamada está ao vivo e por 10
  minutos depois que ela termina. Relatórios posteriores falham com `409`, ou
  com `410` quando a chamada terminou porque o host de mídia dela foi perdido.
* A Polymorfa mantém até 200 relatórios por chamada. Nesse limite, um relatório
  de qualidade substitui o relatório de qualidade mais antigo da mesma conexão.
  Um relatório de qualidade de uma conexão sem nenhum relatório de qualidade
  armazenado, e todo relatório de erro, retornam `202`, mas não são mantidos.
* A Polymorfa autoriza a chamada, não a conexão: qualquer participante como o
  qual a sua credencial pode agir pode enviar relatórios de qualquer
  `connectionId` em uma chamada que essa credencial alcança.

Os relatórios são excluídos junto com o histórico da chamada quando ultrapassam
o [período de retenção de dados de chamadas](#call-data-retention) da sua
equipe. Excluir uma equipe remove o acesso ao histórico dela; isso não garante
a eliminação imediata dos registros retidos.

Uma cadência que mantém o Console atualizado: um relatório de qualidade a cada
15 segundos durante a chamada e um quando a conexão fecha, além de um relatório
de erro sempre que ocorrer um erro.

## Limites de acesso

Toda operação de chamada fica dentro do limite de recursos da credencial.
Chaves de equipe podem controlar chamadas em toda a equipe. Tokens de projeto podem controlar chamadas em qualquer sessão desse projeto. Tokens de cliente
podem controlar chamadas apenas na sessão à qual estão vinculados. Uma chamada
fora desse limite falha com `409` antes que a operação altere o estado da
chamada.

As regras de cliente valem para estabelecimentos de chamada, chamadas feitas e
convites:

* `maxSetupsPerMinute` limita os estabelecimentos de chamada por usuário de
  token de cliente (padrão 10). Cada chamada feita e cada convite contam como
  um estabelecimento. Em uma chamada existente, o primeiro atendimento ou a
  primeira conexão bem-sucedida conta como um estabelecimento; os seguintes na
  mesma chamada não contam.
* `allowedNumber` é uma lista separada por vírgulas de números E.164 para os
  quais um token de cliente pode ligar ou que ele pode convidar.
* `maxConcurrency` limita as chamadas que um usuário de token de cliente mantém
  ao mesmo tempo. Uma chamada ocupa a vaga dela até terminar.

Exceder um limite falha com `429`.

Uma chamada libera a vaga de simultaneidade quando termina, mesmo que o seu
cliente se desconecte sem encerrá-la. Se uma chamada feita ou um
estabelecimento falhar sem um resultado confirmado, a vaga fica reservada por
até quatro horas, porque a chamada ainda pode estar ativa caso a resposta tenha
se perdido.

## Erros

| Status | Código | Significado |
| - | - | - |
| `400` | `invalid_parameter` | Um campo é inválido, ou um token de cliente indicou um `participant`. |
| `403` | `permission_denied` | A credencial não pode agir nesta chamada, sessão ou destino. |
| `403` | [`call_permission_required`](/api/errors#call-permission-required) | O destinatário não concedeu à sua empresa permissão para ligar para ele. |
| `403` | [`number_restricted`](/api/errors#number-restricted) | O WhatsApp restringe o número a ligar para pessoas com quem ele já conversou, e esta pessoa chamada não é uma delas. `Retry-After` informa os segundos até a restrição terminar, quando o WhatsApp informa esse horário. |
| `409` | [`call_claimed`](/api/errors#call-claimed) | Outro participante fez o claim da chamada. |
| `409` | [`call_not_ringing`](/api/errors#call-not-ringing) | A chamada já foi atendida, recusada ou encerrada. |
| `409` | [`connection_limit`](/api/errors#connection-limit) | A chamada não tem espaço para outra conexão. Saia de uma conexão não utilizada primeiro. |
| `409` | [`unsupported_for_connection`](/api/errors#unsupported-for-connection) | A conexão do número não suporta a operação, como vídeo em um número da Cloud API. |
| `409` | `state_conflict` | A chamada não está pronta para esta operação ou está fora do limite da sua credencial. |
| `410` | `resource_gone` | A chamada terminou porque o host de mídia dela não existe mais. |
| `429` | `rate_limit_exceeded` | O limite de inícios de chamadas realizadas da sessão de origem, um limite de chamadas do token de cliente ou o limite de relatórios de uma conexão foi atingido. |
| `501` | `operation_not_supported` | Chamadas realizadas não estão disponíveis neste deployment. |
| `503` | `service_unavailable` | A chamada atingiu a capacidade, a autoridade de chamadas ou as configurações de chamadas estão indisponíveis, ou o host de mídia está em drenagem. |

Os SDKs geram esses erros como erros tipados; `call_claimed` é um
`CallClaimedError`. Consulte [Erros](/api/errors) para o objeto de erro.

### Controles da tabela no Console

A tabela Chamadas abre primeiro. Use os filtros dela para selecionar atividade,
estado, direção, número e datas. Gerenciar troncos SIP, Política de chamadas,
Atualizar chamadas e o botão API ficam ao lado dos filtros. A análise de
chamadas aparece abaixo da tabela.


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