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

# Webhooks

> Receba status de entrega e mensagens dos leads por push, sem polling.

Webhooks são o jeito recomendado de acompanhar o que acontece depois do envio: em vez de consultar `GET /v1/messages/{id}` repetidamente, a Superlead **envia um POST para a sua URL** a cada transição de status e a cada mensagem recebida de um lead.

É assim que se resolve o caso clássico do WhatsApp: a Meta aceita um envio fora da janela de 24 horas (`201 accepted`) e o falha segundos depois, de forma assíncrona — a falha chega no seu webhook, com o motivo.

## Configuração

<Steps>
  <Step title="Cadastre seu endpoint">
    No painel da Superlead, em **Integrações → Webhooks**, adicione a URL HTTPS que vai receber os eventos.
  </Step>

  <Step title="Guarde o signing secret">
    Na criação, você recebe um **signing secret** (`whsec_...`) — ele é exibido nesse momento; guarde em local seguro. É com ele que você verifica a autenticidade de cada entrega.
  </Step>

  <Step title="Responda 2xx rápido">
    Ao receber um evento, responda `2xx` imediatamente e processe de forma assíncrona. Entregas sem `2xx` são reenviadas automaticamente com backoff.
  </Step>
</Steps>

## Evento `whatsapp.message.status`

Disparado a cada transição de status de uma mensagem enviada: `sent`, `delivered`, `read` ou `failed` — **um evento por transição**.

```json theme={null}
{
  "id": "evt_8c1f2a9b4d6e4f0a9b1c2d3e4f5a6b7c",
  "type": "whatsapp.message.status",
  "channel": "whatsapp",
  "apiVersion": "v1",
  "createTime": "2026-07-03T19:03:12.000Z",
  "messageStatus": {
    "wamid": "wamid.HBgMNTU1MzgxNTYwMzQ5FQIAERgU...",
    "status": "failed",
    "recipient": "+555381560349",
    "timestamp": "2026-07-03T19:03:11.000Z",
    "error": { "title": "Re-engagement message", "code": 131047 }
  }
}
```

* **Correlação**: o `wamid` é o mesmo retornado por `POST /v1/messages` — use-o para casar o evento com o envio no seu sistema.
* **`error`**: presente apenas quando `status` é `failed`. O `code` é o código numérico da Meta — o mesmo que aparece como `upstream_code` em `GET /v1/messages/{id}`. Para os significados, veja o [catálogo de erros](/guias/erros).
* Para status de sucesso (`sent`, `delivered`, `read`), `error` vem `null`.

## Evento `whatsapp.message.received`

Disparado quando um lead envia mensagem para o seu número. Exemplo com texto:

```json theme={null}
{
  "id": "evt_9f8e7d6c-1a2b-3c4d-5e6f-7a8b9c0d1e2f",
  "type": "whatsapp.message.received",
  "channel": "whatsapp",
  "apiVersion": "v1",
  "createTime": "2026-07-03T14:30:00.000Z",
  "message": {
    "id": "msg_9f8e7d6c-1a2b-3c4d-5e6f-7a8b9c0d1e2f",
    "wamid": "wamid.HBgMNTU...",
    "wabaId": "1234567890",
    "from": "+5511999999999",
    "fromUserId": null,
    "customerProfile": { "name": "João Silva" },
    "to": "+5511888888888",
    "sendTime": "2026-07-03T14:29:58.000Z",
    "type": "text",
    "text": { "body": "Olá! Quero saber mais." }
  }
}
```

Conforme o `type`, o corpo traz o bloco correspondente no lugar de `text`: `image`/`video`/`document` (com `link`, `mimeType`, `caption`), `audio` (com `voice`), `location`, `contacts` ou `interactive` (resposta de botão/lista). Mensagens vindas de anúncios click-to-WhatsApp incluem também o bloco `referral` com os dados da campanha (`sourceId`, `headline`, `ctwaClid`, `ad`, `campaign`).

<Tip>
  Receber `whatsapp.message.received` também é o sinal de que a **janela de 24 horas reabriu** para aquele lead — a partir dali, mensagens de texto livre voltam a ser aceitas.
</Tip>

## Verificando a assinatura

Cada entrega vem assinada nos headers `svix-id`, `svix-timestamp` e `svix-signature` (infraestrutura [Svix](https://docs.svix.com/receiving/verifying-payloads/how) — mesmo padrão usado por Clerk, Resend e outros). Verifique com a biblioteca da sua linguagem e o signing secret do endpoint:

<CodeGroup>
  ```typescript node.ts theme={null}
  import { Webhook } from "svix";

  const wh = new Webhook(process.env.SUPERLEAD_WEBHOOK_SECRET);
  // rawBody: corpo CRU da requisição (string), antes de qualquer JSON.parse
  const event = wh.verify(rawBody, {
    "svix-id": req.headers["svix-id"],
    "svix-timestamp": req.headers["svix-timestamp"],
    "svix-signature": req.headers["svix-signature"],
  });
  ```

  ```python python.py theme={null}
  from svix.webhooks import Webhook

  wh = Webhook(os.environ["SUPERLEAD_WEBHOOK_SECRET"])
  # payload: corpo cru da requisição (bytes/str), antes do json.loads
  event = wh.verify(payload, headers)
  ```
</CodeGroup>

<Warning>
  Rejeite entregas com assinatura inválida. Sem a verificação, qualquer um que descubra sua URL pode forjar eventos.
</Warning>

## Boas práticas

* **Idempotência**: reentregas acontecem (é a garantia de "pelo menos uma vez"). Use o `id` do evento (`evt_...`) como chave de deduplicação no seu lado.
* **Ordem não garantida**: um `delivered` pode chegar depois de um `read`. Use o `timestamp` do evento, não a ordem de chegada.
* **Webhook + polling**: para fluxos críticos, combine os dois — webhook como caminho principal e `GET /v1/messages/{id}` como reconciliação periódica.
