---
title: "Webhook de Wacaller"
description: "Cada etapa de uma ligação de voz pelo WhatsApp e a resposta de quem atendeu — 15 eventos call.* e 5 question.*, todos na URL que você informa em cada ligação."
lang: pt-BR
canonical: https://doc.apibrasil.io/webhooks/api-wacaller
markdown: https://doc.apibrasil.io/webhooks/api-wacaller.md
source: apibrasil-documentation
---

# Webhook de Wacaller

> Cada etapa de uma ligação de voz pelo WhatsApp e a resposta de quem atendeu — 15 eventos call.* e 5 question.*, todos na URL que você informa em cada ligação.

Diferente dos canais de mensagem, aqui não há URL cadastrada no dispositivo: o webhook_url vai no corpo de cada calls/create, e só a ligação que leva webhook_url emite eventos.

## Como receber

Envie webhook_url no corpo do calls/create (POST /api/v2/wacaller/calls/create). A cada etapa da ligação o Wacaller faz um POST nessa URL com corpo JSON (Content-Type: application/json) e o nome do evento em event — call.* acompanha a chamada; question.* acompanha a resposta do destinatário. A URL pode mudar a cada ligação.

**Onde registrar a URL e o que cai nela**

| Campo | O que recebe | Eventos |
| --- | --- | --- |
| `webhook_url` | TODOS os eventos da ligação — os 15 call.* e os 5 question.*. A URL é informada no corpo de cada calls/create; não existe URL fixa por conta nem por dispositivo. | todos os eventos do canal |

Os eventos deste canal são independentes: não há ordem entre eles.

## Eventos (20)

### `call.created` — Ligação registrada

**Quando dispara:** Quando o Wacaller registra a ligação, ao processar o calls/create — antes de gerar o áudio e antes de responder. Sai junto com call.audio_generating e é sempre o primeiro evento (sequence 1); por isso pode chegar mesmo quando o calls/create termina em erro ou estoura o tempo. Estado call.status: created.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.created". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.created",
    "created_at": "2026-10-30T12:00:00.011342Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 1,
    "call": {
      "status": "created",
      "occurred_at": "2026-10-30T12:00:00.011207Z"
    }
  }
]
```

### `call.audio_generating` — Gerando o áudio

**Quando dispara:** Quando começa a geração do áudio da pergunta a partir do text. É registrado junto com call.created (sequence 2). Estado call.status: producing.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.audio_generating". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.audio_generating",
    "created_at": "2026-10-30T12:00:00.011698Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 2,
    "call": {
      "status": "producing",
      "occurred_at": "2026-10-30T12:00:00.011601Z"
    }
  }
]
```

### `call.audio_ready` — Áudio pronto

**Quando dispara:** Quando o áudio está pronto para iniciar a chamada. Numa nova tentativa do auto_retry, as etapas voltam a chegar a partir daqui. Estado call.status: audio_ready.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.audio_ready". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.audio_ready",
    "created_at": "2026-10-30T12:00:02.481175Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 3,
    "call": {
      "status": "audio_ready",
      "occurred_at": "2026-10-30T12:00:02.480912Z"
    }
  }
]
```

### `call.calling` — Chamada iniciada

**Quando dispara:** Quando a chamada de saída é iniciada no WhatsApp. Estado call.status: calling.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.calling". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.calling",
    "created_at": "2026-10-30T12:00:02.958314Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 4,
    "call": {
      "status": "calling",
      "upstream_id": "exemplo_id_whatsapp",
      "occurred_at": "2026-10-30T12:00:02.950127Z"
    }
  }
]
```

### `call.ringing` — Tocando

**Quando dispara:** Quando o WhatsApp informa que a chamada está tocando no aparelho do destinatário. Depende do sinal recebido do WhatsApp e pode não ocorrer. Estado call.status: ringing.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.ringing". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.ringing",
    "created_at": "2026-10-30T12:00:03.405862Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 5,
    "call": {
      "status": "ringing",
      "upstream_id": "exemplo_id_whatsapp",
      "occurred_at": "2026-10-30T12:00:03.400419Z"
    }
  }
]
```

### `call.connecting` — Conectando

**Quando dispara:** Quando o WhatsApp informa a etapa de conexão da chamada. Depende do sinal recebido do WhatsApp e pode não ocorrer. Estado call.status: connecting.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.connecting". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.connecting",
    "created_at": "2026-10-30T12:00:03.904237Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 6,
    "call": {
      "status": "connecting",
      "upstream_id": "exemplo_id_whatsapp",
      "occurred_at": "2026-10-30T12:00:03.900085Z"
    }
  }
]
```

### `call.answered` — Ligação atendida

**Quando dispara:** Quando o destinatário atende. É o evento que informa o atendimento; o status da ligação passa a active (por isso o nome do evento não repete o estado).

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.answered". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.answered",
    "created_at": "2026-10-30T12:00:04.010Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 7,
    "call": {
      "status": "active",
      "upstream_id": "exemplo_id_whatsapp",
      "occurred_at": "2026-10-30T12:00:04Z"
    }
  }
]
```

### `call.playing` — Reproduzindo

**Quando dispara:** Quando o player começa a tocar a pergunta, depois do atendimento e da preparação da mídia. Estado call.status: playing.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.playing". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.playing",
    "created_at": "2026-10-30T12:00:04.606551Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 8,
    "call": {
      "status": "playing",
      "upstream_id": "exemplo_id_whatsapp",
      "occurred_at": "2026-10-30T12:00:04.600218Z"
    }
  }
]
```

### `call.playback_finished` — Reprodução concluída

**Quando dispara:** Quando o player termina de tocar a pergunta. Não comprova que o destinatário ouviu o áudio. Estado call.status: playback_finished.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.playback_finished". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.playback_finished",
    "created_at": "2026-10-30T12:00:08.204716Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 9,
    "call": {
      "status": "playback_finished",
      "upstream_id": "exemplo_id_whatsapp",
      "occurred_at": "2026-10-30T12:00:08.200373Z"
    }
  }
]
```

### `call.listening` — Ouvindo a resposta

**Quando dispara:** Quando a API começa a captar a resposta falada, nos modos call e either de response_mode. Estado call.status: listening.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.listening". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.listening",
    "created_at": "2026-10-30T12:00:08.215094Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 10,
    "call": {
      "status": "listening",
      "upstream_id": "exemplo_id_whatsapp",
      "occurred_at": "2026-10-30T12:00:08.210652Z"
    }
  }
]
```

### `call.completed` — Ligação concluída

**Quando dispara:** Quando a ligação termina depois de concluir a reprodução. É um estado final. Não comprova que o destinatário ouviu o áudio. Estado call.status: completed.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.completed". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.completed",
    "created_at": "2026-10-30T12:00:12.706389Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 11,
    "call": {
      "status": "completed",
      "upstream_id": "exemplo_id_whatsapp",
      "occurred_at": "2026-10-30T12:00:12.700541Z"
    }
  }
]
```

### `call.ended` — Ligação encerrada

**Quando dispara:** Quando a ligação termina após o atendimento, antes de concluir a reprodução. É um estado final. Estado call.status: ended.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.ended". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.ended",
    "created_at": "2026-10-30T12:00:06.105227Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 9,
    "call": {
      "status": "ended",
      "upstream_id": "exemplo_id_whatsapp",
      "occurred_at": "2026-10-30T12:00:06.100813Z"
    }
  }
]
```

### `call.no_answer` — Sem atendimento

**Quando dispara:** Quando a ligação termina sem que ninguém atenda. Encerra a tentativa; com auto_retry, pode vir uma nova. Pode trazer call.reason. Estado call.status: no_answer.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.no_answer". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.no_answer",
    "created_at": "2026-10-30T12:00:33.406118Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 6,
    "call": {
      "status": "no_answer",
      "upstream_id": "exemplo_id_whatsapp",
      "occurred_at": "2026-10-30T12:00:33.400276Z"
    }
  }
]
```

### `call.failed` — Ligação falhou

**Quando dispara:** Quando a ligação falha na preparação ou no início — um erro na geração do áudio (call.reason tts_failed) termina aqui antes da discagem; call_failed e call_outcome_unknown indicam falha ou desfecho incerto ao iniciar a chamada. Encerra a tentativa; com auto_retry, pode vir uma nova. Estado call.status: failed.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.failed". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.failed",
    "created_at": "2026-10-30T12:00:01.901433Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 3,
    "reason": "tts_failed",
    "call": {
      "status": "failed",
      "reason": "tts_failed",
      "occurred_at": "2026-10-30T12:00:01.901302Z"
    }
  }
]
```

### `call.interrupted` — Ligação interrompida

**Quando dispara:** Quando a execução da ligação é interrompida, inclusive por encerramento ou reinício do servidor: na volta, cada ligação acompanhada que não tinha estado final recebe este evento com call.reason server_restarted. Ligações em andamento não são restabelecidas. É um estado final. Estado call.status: interrupted.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "call.interrupted". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). Não é o instante da etapa: esse é call.occurred_at. |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `sequence` | number | Número do evento dentro da ligação: começa em 1 e cresce a cada etapa, inclusive entre as tentativas do auto_retry. Reenvios mantêm o mesmo valor. Use para ordenar. |
| `reason` | string? | O mesmo valor de call.reason, repetido no topo do evento. Omitido quando não há motivo. |
| `call.status` | string | Estado da ligação nesta etapa. |
| `call.upstream_id` | string? | Identifica a chamada no WhatsApp. Só aparece depois que a chamada existe lá; omitido antes disso. |
| `call.reason` | string? | Motivo da etapa, em falhas e encerramentos (como tts_failed ou server_restarted). Omitido quando não há. |
| `call.occurred_at` | string | Quando a etapa ocorreu (ISO 8601). created_at, no topo, é a criação do evento persistido. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "c2c14fd0-497d-44e9-a9a7-61bbac8cf080",
    "event": "call.interrupted",
    "created_at": "2026-10-30T12:03:10.002871Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "sequence": 9,
    "reason": "server_restarted",
    "call": {
      "status": "interrupted",
      "upstream_id": "exemplo_id_whatsapp",
      "reason": "server_restarted",
      "occurred_at": "2026-10-30T12:03:10.002745Z"
    }
  }
]
```

### `question.answered` — Pergunta respondida

**Quando dispara:** Quando a resposta do destinatário é salva: fala ou dígito do teclado na ligação, ou texto ou voz no chat do WhatsApp. Texto (e dígito) já vem em response.text; áudio vem em response.audio_path, e com transcribe ligado a transcrição chega depois, em question.transcribed. Diferente de call.answered, que só informa o atendimento.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "question.answered". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `response.id` | string | Identificador da resposta. É o que se usa em GET /api/v2/wacaller/responses/{id}/audio. |
| `response.question_id` | string | O id da pergunta a que a resposta pertence. |
| `response.source` | string | Onde a resposta chegou: call (na ligação) ou chat (mensagem do WhatsApp). |
| `response.source_id` | string | Identifica a origem: o id da ligação (source call) ou da mensagem (source chat). |
| `response.type` | string | text (texto do chat, ou dígito do teclado na ligação) ou audio. |
| `response.text` | string? | O texto da resposta, em type text. Omitido em áudio: a transcrição chega depois, em question.transcribed. |
| `response.audio_path` | string? | Caminho interno do áudio (/responses/{id}/audio). Não é um link público: baixe pelo gateway. Só em respostas de áudio. |
| `response.mime_type` | string? | Formato do áudio: audio/wav na ligação; no chat, o formato recebido (como OGG). Só em áudio. |
| `response.size_bytes` | number? | Tamanho do áudio em bytes. Só em áudio. |
| `response.received_at` | string | Quando a resposta foi recebida (ISO 8601). |
| `response.transcription_status` | string | pending (áudio com transcribe ligado) ou disabled (resposta de texto, ou transcribe: false). |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "8d652c98-2935-40f9-a39a-aafc240aaefd",
    "event": "question.answered",
    "created_at": "2026-10-30T12:00:11.952604Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "response": {
      "id": "963b6409-bb91-43eb-9106-3d8621811467",
      "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
      "source": "call",
      "source_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
      "type": "audio",
      "audio_path": "/responses/963b6409-bb91-43eb-9106-3d8621811467/audio",
      "mime_type": "audio/wav",
      "size_bytes": 96044,
      "received_at": "2026-10-30T12:00:10.903518Z",
      "transcription_status": "pending"
    }
  }
]
```

### `question.transcribed` — Transcrição concluída

**Quando dispara:** Quando a transcrição do áudio termina (transcribe: true). response.text traz o resultado e response.transcription_status vale completed. Chega com outro id e o mesmo response.id do question.answered.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "question.transcribed". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `response.id` | string | Identificador da resposta. É o que se usa em GET /api/v2/wacaller/responses/{id}/audio. |
| `response.question_id` | string | O id da pergunta a que a resposta pertence. |
| `response.source` | string | Onde a resposta chegou: call (na ligação) ou chat (mensagem do WhatsApp). |
| `response.source_id` | string | Identifica a origem: o id da ligação (source call) ou da mensagem (source chat). |
| `response.type` | string | audio: só resposta de áudio é transcrita. |
| `response.text` | string | O resultado da transcrição. |
| `response.audio_path` | string | Caminho interno do áudio (/responses/{id}/audio). Não é um link público: baixe pelo gateway. |
| `response.mime_type` | string | Formato do áudio: audio/wav na ligação; no chat, o formato recebido (como OGG). |
| `response.size_bytes` | number | Tamanho do áudio em bytes. |
| `response.received_at` | string | Quando a resposta foi recebida (ISO 8601). |
| `response.transcription_status` | string | completed. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "4b1e7c52-90d3-4f6a-8e21-5c7a3d9f0b14",
    "event": "question.transcribed",
    "created_at": "2026-10-30T12:00:13.418077Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "response": {
      "id": "963b6409-bb91-43eb-9106-3d8621811467",
      "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
      "source": "call",
      "source_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
      "type": "audio",
      "text": "Sim, confirmo.",
      "audio_path": "/responses/963b6409-bb91-43eb-9106-3d8621811467/audio",
      "mime_type": "audio/wav",
      "size_bytes": 96044,
      "received_at": "2026-10-30T12:00:10.903518Z",
      "transcription_status": "completed"
    }
  }
]
```

### `question.transcription_failed` — Transcrição falhou

**Quando dispara:** Quando a transcrição do áudio falha ou volta vazia. O áudio original continua disponível; response.transcription_error traz o código e response.text não vem.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "question.transcription_failed". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `response.id` | string | Identificador da resposta. É o que se usa em GET /api/v2/wacaller/responses/{id}/audio. |
| `response.question_id` | string | O id da pergunta a que a resposta pertence. |
| `response.source` | string | Onde a resposta chegou: call (na ligação) ou chat (mensagem do WhatsApp). |
| `response.source_id` | string | Identifica a origem: o id da ligação (source call) ou da mensagem (source chat). |
| `response.type` | string | audio: só resposta de áudio é transcrita. |
| `response.audio_path` | string | Caminho interno do áudio (/responses/{id}/audio). Não é um link público: baixe pelo gateway. |
| `response.mime_type` | string | Formato do áudio: audio/wav na ligação; no chat, o formato recebido (como OGG). |
| `response.size_bytes` | number | Tamanho do áudio em bytes. |
| `response.received_at` | string | Quando a resposta foi recebida (ISO 8601). |
| `response.transcription_status` | string | failed. |
| `response.transcription_error` | string | O código do erro: transcription_failed (a transcrição falhou ou voltou vazia) ou transcription_not_configured (a instalação não tem transcrição configurada). |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "a73f2d18-6c45-4e9b-b0d2-91e8f4c6a357",
    "event": "question.transcription_failed",
    "created_at": "2026-10-30T12:00:13.418077Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "response": {
      "id": "963b6409-bb91-43eb-9106-3d8621811467",
      "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
      "source": "call",
      "source_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
      "type": "audio",
      "audio_path": "/responses/963b6409-bb91-43eb-9106-3d8621811467/audio",
      "mime_type": "audio/wav",
      "size_bytes": 96044,
      "received_at": "2026-10-30T12:00:10.903518Z",
      "transcription_status": "failed",
      "transcription_error": "transcription_failed"
    }
  }
]
```

### `question.expired` — Pergunta expirada

**Quando dispara:** Quando a pergunta termina sem resposta válida. reason traz o motivo: response_timeout (acabou o prazo de response_timeout_seconds), no_response (a ligação terminou sem resposta, no modo call), no_speech (nenhuma fala detectada na ligação, no modo call) ou cancelled (calls/{id}/hangup cancelou uma nova tentativa ainda pendente).

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "question.expired". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `reason` | string | O motivo do desfecho. Sempre presente neste evento. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "e5d09a6b-3f72-4c18-9a4e-07b2c8d1f963",
    "event": "question.expired",
    "created_at": "2026-10-30T12:01:13.006231Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "reason": "response_timeout"
  }
]
```

### `question.failed` — Pergunta falhou

**Quando dispara:** Quando a pergunta não pode continuar; reason traz o motivo. Por exemplo: call_ended_before_question (a ligação terminou antes de a pergunta acabar de tocar), tts_failed (falha ao gerar o áudio), call_failed ou call_outcome_unknown (falha ou desfecho incerto ao iniciar a chamada), billing_unavailable (cobrança indisponível na instalação), audio_download_failed ou audio_download_not_configured (não foi possível obter o áudio da resposta do chat) e server_restarted (reinício do servidor). Com auto_retry, só chega quando não há nova tentativa.

**Campos**

| Nome | Tipo | Significado |
| --- | --- | --- |
| `id` | string | Identificador do evento. É o mesmo valor do cabeçalho X-Webhook-Id; reenvios mantêm o mesmo id. |
| `event` | string | O nome do evento, como em "question.failed". |
| `created_at` | string | Quando o evento foi criado (ISO 8601). |
| `question_id` | string | O id retornado em calls/create. Igual a call_id; NÃO é o upstream_id do WhatsApp. |
| `call_id` | string | Identifica a ligação no histórico. Igual a question_id. |
| `phone` | string | O número chamado, sem o + inicial. |
| `reason` | string | O motivo do desfecho. Sempre presente neste evento. |

**Corpo de exemplo, como chega**

```json
[
  {
    "id": "1f8c4e27-b6d3-49a0-8c5f-d2e7a9b03c41",
    "event": "question.failed",
    "created_at": "2026-10-30T12:00:20.114862Z",
    "question_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "call_id": "59f1cdbc-ecfa-4d86-b4a0-53021c81649c",
    "phone": "5511999999999",
    "reason": "audio_download_failed"
  }
]
```

## O que costuma pegar

- **Nesta fase não há assinatura para você verificar** — Cada entrega leva X-Webhook-Id, X-Webhook-Timestamp (o instante da tentativa, em segundos Unix) e X-Webhook-Signature (sha256= seguido de um HMAC-SHA256 em hexadecimal). Esse HMAC usa o segredo da instalação do Wacaller — um só para a instalação inteira, não um por cliente —, e nesta fase ele não é compartilhado com o cliente. Não há como validar a assinatura: ignore X-Webhook-Signature. Proteja o receptor com uma URL secreta (um token longo no caminho) e deduplique por X-Webhook-Id.
- **Até 8 tentativas, e só 2xx conta como entregue** — Cada evento tem até 8 tentativas no total: a primeira entrega e até 7 reenvios. Depois da n-ésima falha, a próxima tentativa espera 5 s × 2^(n-1) — 5, 10, 20, 40, 80, 160 e 320 segundos —, sujeito à carga do servidor. Qualquer resposta fora de 2xx conta como falha, inclusive redirecionamento (3xx), que não é seguido. Depois da oitava falha o evento fica como failed: não há reenvio ilimitado.
- **Destinos privados e locais são recusados** — O webhook_url precisa ser HTTP ou HTTPS, sem usuário, senha nem fragmento, com até 2.048 caracteres, e o host precisa resolver no DNS já no calls/create — senão a ligação é recusada com 400. Localhost e redes privadas são bloqueados, inclusive depois de resolver o DNS, e o destino é conferido de novo a cada tentativa. Um receptor em rede interna não recebe nada.
- **Só a ligação com webhook_url emite eventos** — Sem webhook_url não há webhook nenhum — nem na ligação comum, nem na pergunta (type "question"), cujo resultado fica só para consulta em questions/{id}. Informar webhook_url liga todos os eventos daquela ligação, sem outra opção, e a faz seguir o fluxo de pergunta mesmo sem type: depois de tocar o text, o Wacaller espera a resposta (response_mode padrão either, prazo padrão de 60 s). Os campos de resposta (response_mode, response_timeout_seconds, transcribe) exigem type "question" ou webhook_url.
- **A ordem de chegada não é garantida: ordene por sequence** — Eventos distintos podem chegar fora de ordem. Nos call.*, sequence começa em 1 e cresce por ligação (o mesmo call_id); use-o para não deixar uma entrega atrasada sobrescrever um estado mais novo. Os question.* não trazem sequence — atualize a resposta pelo response.id.
- **Os eventos seguem o que foi observado, não um roteiro** — ringing e connecting dependem do que o WhatsApp informa, e os estados finais (completed, ended, no_answer, failed, interrupted) são alternativas: cada tentativa termina em um deles. Com auto_retry, um no_answer ou failed pode ser seguido de nova tentativa da mesma ligação: as etapas voltam a chegar (call.audio_ready, call.calling…) com id novo, o mesmo call_id e sequence continuando a crescer. completed, playing e playback_finished não provam que o destinatário ouviu o áudio, e o 202 do calls/create não confirma que alguém atendeu.

## Boas práticas

- **Deduplique por X-Webhook-Id** — O cabeçalho X-Webhook-Id é igual ao id do evento. Reenvios mantêm o id e o corpo (só o timestamp e a assinatura mudam). Guarde os ids já processados e responda 2xx para os repetidos.
- **Use uma URL secreta e HTTPS** — Sem assinatura verificável, o caminho da URL é o seu único segredo. Gere um token longo e aleatório para ele, sirva por HTTPS e não o divulgue em logs ou em páginas públicas.
- **Persista, depois responda 2xx** — Grave o evento e responda 2xx logo em seguida. O Wacaller espera no máximo 10 segundos pelos cabeçalhos da resposta e 15 segundos pela troca inteira; passado isso, a tentativa conta como falha e o MESMO evento volta mais tarde — um receptor lento vira cópias duplicadas.
- **O áudio da resposta se baixa pelo gateway** — response.audio_path é um caminho interno do Wacaller (/responses/{id}/audio) e não um link público. Para baixar, chame GET /api/v2/wacaller/responses/{id}/audio com o response.id, Authorization: Bearer e DeviceToken; a resposta é o próprio arquivo, sem envelope JSON. O áudio de ligação é WAV; mensagens de voz do chat mantêm o formato recebido (como OGG) — escolha a extensão por response.mime_type.
- **Acompanhe call_id e question_id** — call_id e question_id têm o mesmo valor: o id retornado em calls/create. Ele não é o call.upstream_id, que identifica a chamada no WhatsApp e só aparece depois que a chamada existe lá. A transcrição chega em outro evento, com outro id e o mesmo response.id; preserve uma transcrição concluída ao receber um evento anterior.

## Receptores de exemplo

**Node**

```node
const vistos = new Set() // em produção: banco ou Redis

app.post("/webhooks/wacaller/SEU_TOKEN_SECRETO", express.json(), (req, res) => {
  const idDoEvento = req.get("X-Webhook-Id")

  // Sem assinatura verificável nesta fase: a URL secreta é a proteção.
  if (vistos.has(idDoEvento)) return res.sendStatus(200) // reenvio
  vistos.add(idDoEvento)

  res.sendStatus(200) // qualquer 2xx, em até 10 s; sem ele, o mesmo evento volta (até 8 tentativas)

  const { event, call_id, sequence, call, response, reason } = req.body

  switch (event) {
    case "call.answered":
      console.log("atendeu", call_id, `#${sequence}`)
      break
    case "call.no_answer":
    case "call.failed":
      console.log("não completou", call_id, call?.reason) // com auto_retry, pode vir outra tentativa
      break
    case "question.answered":
      if (response.type === "text") console.log("resposta", response.text)
      else baixarAudio(response.id) // GET /api/v2/wacaller/responses/{id}/audio
      break
    case "question.transcribed":
      console.log("transcrição", response.text)
      break
    case "question.expired":
      console.log("sem resposta:", reason) // response_timeout, no_response, no_speech, cancelled
      break
    default:
      console.log("evento", event)
  }
})
```

**Python**

```python
vistos = set()  # em produção: banco ou Redis

@app.post("/webhooks/wacaller/SEU_TOKEN_SECRETO")
def webhook_wacaller():
    # Sem assinatura verificável nesta fase: a URL secreta é a proteção.
    id_do_evento = request.headers.get("X-Webhook-Id")
    if id_do_evento in vistos:
        return "", 200  # reenvio
    vistos.add(id_do_evento)

    corpo = request.get_json(force=True)
    evento = corpo["event"]

    if evento == "call.answered":
        print("atendeu", corpo["call_id"], corpo["sequence"])
    elif evento in ("call.no_answer", "call.failed"):
        print("não completou", corpo["call_id"], corpo["call"].get("reason"))  # com auto_retry, pode vir outra tentativa
    elif evento == "question.answered":
        resposta = corpo["response"]
        if resposta["type"] == "text":
            print("resposta", resposta["text"])
        else:
            baixar_audio(resposta["id"])  # GET /api/v2/wacaller/responses/{id}/audio
    elif evento == "question.transcribed":
        print("transcrição", corpo["response"]["text"])
    elif evento == "question.expired":
        print("sem resposta:", corpo["reason"])  # response_timeout, no_response, no_speech, cancelled
    else:
        print("evento", evento)

    return "", 200  # qualquer 2xx, em até 10 s; sem ele, o mesmo evento volta (até 8 tentativas)
```

**PHP**

```php
<?php

// Sem assinatura verificável nesta fase: a URL secreta é a proteção.
$idDoEvento = $_SERVER['HTTP_X_WEBHOOK_ID'] ?? '';
if (jaProcessado($idDoEvento)) { // em produção: banco ou Redis
    http_response_code(200); // reenvio
    exit;
}
marcarProcessado($idDoEvento);

$corpo = json_decode(file_get_contents('php://input'), true);

http_response_code(200); // qualquer 2xx, em até 10 s; sem ele, o mesmo evento volta (até 8 tentativas)

switch ($corpo['event']) {
    case 'call.answered':
        error_log('atendeu ' . $corpo['call_id'] . ' #' . $corpo['sequence']);
        break;
    case 'call.no_answer':
    case 'call.failed':
        error_log('não completou ' . ($corpo['call']['reason'] ?? '')); // com auto_retry, pode vir outra tentativa
        break;
    case 'question.answered':
        if ($corpo['response']['type'] === 'text') {
            error_log('resposta ' . $corpo['response']['text']);
        } else {
            baixarAudio($corpo['response']['id']); // GET /api/v2/wacaller/responses/{id}/audio
        }
        break;
    case 'question.transcribed':
        error_log('transcrição ' . $corpo['response']['text']);
        break;
    case 'question.expired':
        error_log('sem resposta: ' . $corpo['reason']); // response_timeout, no_response, no_speech, cancelled
        break;
    default:
        error_log('evento ' . $corpo['event']);
}
```

## Ver também

- Webhooks: [Webhooks](https://doc.apibrasil.io/webhooks.md)
- Índice da documentação: https://doc.apibrasil.io/llms.txt
- Versão HTML desta página: https://doc.apibrasil.io/webhooks/api-wacaller
