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

# Visão geral dos webhooks

> Como o ThunderPhone entrega eventos em tempo real, como verificar assinaturas e como os modelos de entrega legados e baseados em endpoints se comparam.

O ThunderPhone envia solicitações HTTP `POST` ao seu servidor quando eventos
ocorrem durante uma chamada — uma chamada recebida começa, uma chamada termina, uma execução
de avaliação é concluída, um alerta é disparado e assim por diante. Há **dois modelos
de entrega**:

<CardGroup cols={2}>
  <Card title="Endpoints de webhook (recomendado)" icon="bolt" href="/pt/webhooks/endpoints">
    Várias URLs, segredos por endpoint, filtros de eventos por endpoint
    e novas tentativas automáticas.
    Gerencie por meio de `GET/POST/PATCH/DELETE /v1/developer/webhook-endpoints`.
  </Card>

  <Card title="Webhook legado de URL única" icon="link" href="/api-reference/organizations#legacy-single-url-webhook">
    Uma URL por organização. Transporta os eventos do ciclo de vida da chamada, incluindo os
    intercâmbios de configuração **bloqueantes**. Gerenciado em `GET/PUT /v1/webhook`.
  </Card>
</CardGroup>

Todos os dez tipos de evento no [catálogo de eventos](/pt/webhooks/events) são
entregues por endpoints de webhook. Os seis eventos do ciclo de vida da chamada
(`telephony.incoming`, `telephony.complete`, `telephony.tool`,
`web.incoming`, `web.complete`, `web.tool`) **também** são enviados ao
webhook legado de URL única — se você tiver uma URL legada e um endpoint
correspondente, receberá o evento em **ambos** os caminhos. O comportamento
bloqueante (o [intercâmbio de configuração de `telephony.incoming` / `web.incoming`](/pt/webhooks/call-incoming)
e o [despacho de ferramentas](/pt/tools/overview) no modo webhook)
existe exclusivamente no caminho legado; cada entrega a um endpoint é uma
notificação sem aguardar resposta.

## Formato do payload

As entregas ao endpoint são um objeto JSON com `data`, `event_id` e
`type`:

```json theme={null}
{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}
```

`event_id` é único para cada evento emitido. Ele é idêntico em novas tentativas
**e** em todos os endpoints que recebem o evento — faça a deduplicação por ele.

O webhook legado de URL única envia o mesmo `type` e `data`, mas
**sem** `event_id`:

```json theme={null}
{
  "type": "telephony.incoming",
  "data": { "call_id": 987654321, "from_number": "+14155550199", "to_number": "+15551234567" }
}
```

Na transmissão, cada corpo é serializado de forma canônica — chaves ordenadas
alfabeticamente, sem espaços em branco, UTF-8. Os exemplos formatados nestes
documentos servem apenas para facilitar a leitura.

Consulte o [Catálogo de eventos](/pt/webhooks/events) para ver a lista completa de tipos
de evento e campos de payload.

## Verificação de assinatura

Cada solicitação inclui uma assinatura HMAC-SHA256 sobre o **corpo bruto da solicitação** no cabeçalho `X-ThunderPhone-Signature`. A chave de assinatura é o `secret` do endpoint (ou o `secret` de webhook no nível da sua organização para entregas legadas).

### Etapas

1. Leia o corpo bruto da solicitação **antes** de qualquer análise.
2. Calcule `hmac_sha256(secret, body).hexdigest()`.
3. Compare em tempo constante com o cabeçalho `X-ThunderPhone-Signature`.

Assinamos exatamente os bytes que transmitimos, e esses bytes correspondem à serialização JSON canônica (chaves ordenadas, separadores compactos). Portanto, a verificação com base no corpo bruto sempre funciona — e, se o seu framework fornecer apenas o JSON analisado, serializá-lo novamente com chaves ordenadas e separadores compactos produzirá bytes idênticos. Ambas as abordagens são abordadas no [guia de verificação](/pt/guides/verify-webhook-signatures).

<CodeGroup>
  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_signature(body: bytes, signature: str, secret: str) -> bool:
      expected = hmac.new(
          secret.encode("utf-8"),
          body,
          hashlib.sha256,
      ).hexdigest()
      return hmac.compare_digest(expected, signature or "")

  # Example Flask handler
  from flask import Flask, request, abort
  app = Flask(__name__)

  @app.post("/thunderphone-webhook")
  def handle():
      body = request.get_data()
      sig = request.headers.get("X-ThunderPhone-Signature", "")
      if not verify_signature(body, sig, WEBHOOK_SECRET):
          abort(401)
      event = request.get_json()
      # dispatch on event["type"] …
      return "", 204
  ```

  ```javascript Node.js (Express) theme={null}
  import crypto from "node:crypto";
  import express from "express";

  function verifySignature(body, signature, secret) {
    const expected = crypto
      .createHmac("sha256", secret)
      .update(body)
      .digest("hex");
    if (!signature || expected.length !== signature.length) return false;
    return crypto.timingSafeEqual(
      Buffer.from(expected),
      Buffer.from(signature),
    );
  }

  const app = express();
  app.post(
    "/thunderphone-webhook",
    express.raw({ type: "application/json" }),
    (req, res) => {
      const sig = req.header("X-ThunderPhone-Signature") || "";
      if (!verifySignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
        return res.sendStatus(401);
      }
      const event = JSON.parse(req.body.toString("utf8"));
      // dispatch on event.type …
      res.sendStatus(204);
    },
  );
  ```
</CodeGroup>

## Semântica de entrega

Estas semânticas se aplicam a entregas de **endpoint**. O webhook legado de URL única
é uma única tentativa síncrona, sem novas tentativas.

<AccordionGroup>
  <Accordion title="Novas tentativas">
    Cada evento é tentado uma vez imediatamente. Qualquer resposta `2xx`
    confirma a entrega. Em qualquer outro resultado (não-2xx,
    erro de conexão, tempo limite), fazemos novas tentativas após **1 min, 5 min, 30 min, 2 h, 6 h,
    12 h e 24 h da primeira tentativa** — 8 tentativas ao longo de
    24 horas. Se todas as tentativas falharem, a entrega é interrompida e o endpoint
    é marcado como `status="failing"` em
    [endpoints de webhook](/pt/webhooks/endpoints). Retorne `2xx` assim que
    o payload for aceito de forma durável; processe de maneira assíncrona.
  </Accordion>

  <Accordion title="Ordenação">
    A ordenação de entrega é feita conforme possível. Na prática, entregamos na
    ordem em que os eventos são emitidos, mas novas tentativas podem reordenar eventos em caso de falha.
    Sempre elimine duplicatas e reconcilie por `call_id` / id do objeto.
  </Accordion>

  <Accordion title="Duplicatas">
    A entrega é **pelo menos uma vez**: uma nova tentativa após uma resposta que nunca
    recebemos pode duplicar um evento. Cada nova tentativa carrega o mesmo
    `event_id`, portanto armazene os ids processados e ignore repetições. `event_id` também é
    compartilhado entre endpoints — dois endpoints inscritos no
    mesmo evento recebem o mesmo `event_id`.
  </Accordion>

  <Accordion title="Tempos limite">
    As entregas de endpoint têm um tempo limite de **30 s** por tentativa. No
    caminho legado, as solicitações bloqueantes que orientam o comportamento de chamadas ao vivo — a
    troca de configuração [`telephony.incoming` / `web.incoming`](/pt/webhooks/call-incoming) —
    expiram após **10 s**, mas uma resposta lenta atrasa o atendimento da chamada,
    portanto busque responder em poucos segundos. A [execução de ferramentas](/pt/tools/overview) no modo webhook permite 20 s.
  </Accordion>

  <Accordion title="IPs de origem">
    Webhooks de saída são originados da faixa de IPs de nuvem do ThunderPhone.
    Se seu firewall exigir uma lista de permissões, entre em contato com o suporte e compartilharemos
    as faixas atuais.
  </Accordion>
</AccordionGroup>

## Como escolher entre webhooks legados e baseados em endpoint

| Recurso                          | Legado (`/v1/webhook`)                                                    | Endpoints (`/v1/developer/webhook-endpoints`) |
| -------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------- |
| Número de URLs                   | 1 por organização                                                         | Vários por organização                        |
| Cobertura de eventos             | Apenas `telephony.*` / `web.*`                                            | Todos os 10 tipos de evento                   |
| Filtro de eventos                | —                                                                         | Por endpoint                                  |
| Novas tentativas                 | Nenhuma                                                                   | 8 tentativas em 24 h                          |
| Envelope                         | `type` + `data`                                                           | `type` + `data` + `event_id`                  |
| Rotação de segredo               | Substitui o segredo único                                                 | Segredo por endpoint                          |
| Desativar sem excluir            | —                                                                         | `status=disabled`                             |
| Visibilidade de status           | —                                                                         | `active` / `disabled` / `failing`             |
| Troca de configuração bloqueante | Sim ([`telephony.incoming` / `web.incoming`](/pt/webhooks/call-incoming)) | Nunca — apenas notificações                   |
| Ideal para                       | Configuração dinâmica de chamadas                                         | Consumo de eventos em produção                |

Novas integrações devem consumir eventos por meio de webhooks baseados em
endpoint. Mantenha (ou adicione) uma URL legada apenas se você configurar chamadas
dinamicamente no momento do atendimento ou usar execução de ferramentas no modo webhook — essas
trocas de solicitação/resposta são executadas apenas no caminho legado.

***

## Relacionados

<CardGroup cols={2}>
  <Card title="Catálogo de eventos" icon="list" href="/pt/webhooks/events">
    Todos os tipos de evento e seus payloads.
  </Card>

  <Card title="Endpoints de webhook" icon="bolt" href="/pt/webhooks/endpoints">
    Gerencie vários endpoints, filtros de eventos e segredos.
  </Card>

  <Card title="telephony.incoming / web.incoming" icon="phone" href="/pt/webhooks/call-incoming">
    A solicitação bloqueante à qual seu servidor deve responder para configurar chamadas.
  </Card>

  <Card title="telephony.complete / web.complete" icon="phone" href="/pt/webhooks/call-complete">
    Payload pós-chamada com transcrição, gravação e métricas.
  </Card>
</CardGroup>
