> ## 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.

# Webhooks

> Crie endpoints de webhook, verifique assinaturas, teste receptores, troque segredos e tente novamente entregas.

Webhooks enviam eventos, como mensagens recebidas, para o seu endpoint HTTPS.
Gerencie endpoints com `webhooks` em um cliente de equipe ou de projeto e
verifique cada solicitação com os utilitários de `webhooks`.

| Tarefa | Permissão |
| - | - |
| Listar e ler endpoints | `webhooks:read` |
| Criar, atualizar, excluir, testar e trocar segredos | `webhooks:manage` |
| Ler entregas e tentativas | `webhook-deliveries:read` |
| Tentar novamente uma entrega | `webhook-deliveries:retry` |

Endpoints de projeto recebem os eventos dos Números do projeto, como mensagens e
chamadas. Endpoints de equipe recebem eventos da equipe, como eventos do ciclo
de vida de Clientes e campanhas. Veja [Webhooks](/api/webhooks) para todos os
tipos de evento.

## Crie um endpoint

```typescript theme={null}
const created = await project.webhooks.create({
  url: "https://example.com/polymorfa/webhooks",
  eventTypes: ["message.received", "session.status"],
});

console.log(created.data.webhook.id);
const secret = created.data.secret; // Shown once. Store it now.
```

Guarde o segredo de assinatura no seu gerenciador de segredos. A API o retorna
apenas nesta resposta. `secretAvailable` é `false` quando a resposta não pode
incluí-lo.

Altere um endpoint com `update(webhookId, { ... })`. Defina `enabled: false`
para pausar as entregas e chame `delete(webhookId)` para removê-lo.

## Verifique um webhook

Toda entrega traz um cabeçalho `x-webhook-signature`. Verifique-o com os bytes
exatos da solicitação antes de ler o evento:

```typescript theme={null}
import { PolymorfaValidationError, WebhookSignatureError, webhooks } from "@polymorfa/sdk";

export default {
  async fetch(request: Request, env: { WEBHOOK_SECRET: string }): Promise<Response> {
    if (request.method !== "POST") return new Response(null, { status: 405 });

    const body = await request.arrayBuffer();
    try {
      const event = await webhooks.verify({
        body,
        signature: request.headers.get("x-webhook-signature") ?? "",
        secret: env.WEBHOOK_SECRET,
      });
      console.log(event.id, event.event);
      return Response.json({ received: event.id });
    } catch (error) {
      if (error instanceof WebhookSignatureError || error instanceof PolymorfaValidationError) {
        return new Response("Invalid webhook", { status: 400 });
      }
      throw error;
    }
  },
};
```

Este handler roda como um Cloudflare Worker. A mesma chamada `webhooks.verify`
funciona em qualquer handler que consiga ler o corpo bruto, incluindo Node.js,
Deno e Bun. Leia o corpo como bytes. Interpretar o JSON e serializá-lo de novo
muda os bytes e invalida a assinatura.

`webhooks.verify` rejeita uma assinatura inválida antes de interpretar o corpo.
Use `webhooks.verifySignature` para obter um resultado `true` ou `false` sem
interpretar o corpo.

As novas tentativas de entrega reutilizam o `id` do evento. Guarde os IDs já
processados e ignore as repetições.

## Trate cada tipo de evento

`isEvent` restringe um evento ao tipo da carga dele:

```typescript theme={null}
import { isEvent, type WebhookEvent } from "@polymorfa/sdk";

export function route(event: WebhookEvent) {
  if (isEvent(event, "message.received")) {
    console.log("message", event.session, event.payload);
  } else if (isEvent(event, "message.failed")) {
    console.log("failed", event.payload.error, event.payload.code);
  } else if (isEvent(event, "campaign.stopped")) {
    console.log("campaign stopped", event.payload.campaignId);
  } else {
    console.log("other event", event.event);
  }
}
```

Tipos de evento que o SDK instalado não conhece continuam sendo verificados. A
carga deles não é tipada.

## Envie uma entrega de teste

```typescript theme={null}
const test = await project.webhooks.test(
  "<webhook-id>",
  { eventType: "message.received" },
  { idempotencyKey: crypto.randomUUID() },
);

console.log(test.data.eventId, test.data.deliveryId);
```

Em um cliente de equipe, `test` aceita apenas `eventType`. Em um cliente de
projeto, ele também aceita um `body` de evento nativo codificado em base64,
junto com o `sessionId` de um Número desse projeto. Veja
[Envie uma entrega de teste](/api/webhooks#send-a-test-delivery).

## Teste seu receptor sem a API

`webhooks.createFixture` assina o corpo de um evento localmente, sem chamar a
API. Use-o em testes unitários:

```typescript theme={null}
import { webhooks } from "@polymorfa/sdk";

const secret = "test-signing-secret";
const fixture = await webhooks.createFixture({
  event: {
    id: "evt_test_1",
    session: "number_test",
    timestamp: "2026-09-27T00:00:00Z",
    event: "session.status",
    payload: { status: "connected" },
  },
  secret,
});

// Send fixture.body with fixture.headers to your handler under test.
const event = await webhooks.verify({
  body: fixture.body,
  signature: fixture.headers["x-webhook-signature"],
  secret,
});
console.log(event.event); // "session.status"
```

Passe `fixture.body` para o seu handler sem alterações. Use um segredo de
teste, nunca um de produção.

As fixtures abrangem apenas o formato nativo. Endpoints que usam o formato da
Meta são assinados com o cabeçalho `X-Hub-Signature-256`. Veja
[Formatos de entrega](/api/webhooks#delivery-formats).

## Troque um segredo de assinatura

```typescript theme={null}
const rotated = await project.webhooks.rotateSecret("<webhook-id>", {
  overlapSeconds: 3600,
});

const newSecret = rotated.data.secret;
console.log(rotated.data.secretMetadata.previousValidUntil);
```

A resposta retorna o novo segredo uma única vez. O segredo anterior continua
válido até `previousValidUntil`. Faça o deploy do novo segredo no seu receptor
antes disso.

## Inspecione entregas e tente novamente

```typescript theme={null}
for await (const delivery of await project.webhookDeliveries.list({ status: "failed" })) {
  console.log(delivery.id, delivery.eventId, delivery.lastOutcome?.statusCode);

  if (delivery.capabilities.retryable) {
    await project.webhookDeliveries.retry(delivery.id);
  }
}
```

`listAttempts(deliveryId)` e `retrieveAttempt(deliveryId, attemptId)` mostram
cada tentativa. Uma tentativa que falhou inclui um trecho com dados ocultados da
resposta do seu endpoint. Para enviar de novo um evento mais antigo, veja
[Reenvie um evento](/pt-br/sdks/typescript/events#reenvie-um-evento).

## Mudanças nos recursos do Número

O beta de recursos por número, para equipes inscritas, emite
`session.capabilities_updated`. As versões do SDK que incluem esse evento
expõem `SessionCapabilitiesUpdatedPayload` e o restringem com
`isEvent(event, "session.capabilities_updated")`. A carga contém a visão
canônica dos recursos do número e `changedKeys`. Veja
[Recursos por número](/pt-br/guides/numbers/number-capabilities) para as regras de
elegibilidade e de eventos em fila. Verifique a assinatura antes de examinar a
carga.

## Notificações de identidade da API oficial

Os webhooks da API oficial mantêm os metadados opcionais `parentBsuid`. Um
evento `contact.update` com `identityChange` informa a nova identidade e a
identidade anterior, quando o WhatsApp a fornece. Não mescle registros de
contato só porque um ID pai coincide. Veja
[Identidades de usuário](/api/user-identities#official-api-identity-changes)
para os campos transmitidos e as restrições do ID pai. Fixe uma versão do SDK
cujos tipos de webhook incluam esses campos antes de depender do acesso tipado.


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