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

# Webhooks de alerta: alertas da equipe no seu endpoint

> O Didit envia alertas ao seu endpoint HTTPS: revisões, monitoramento AML, faturamento, webhooks com falha, status. HMAC-SHA256, reenvios por 24 horas.



Prompt de webhook de alertas
        Copiar o prompt para a área de transferência



      
      

\<pre>

\`Add an endpoint that receives Didit alert webhooks (team alerts, not verification results).

\## What to build
A public HTTPS POST endpoint (e.g. /api/didit/alerts) that:
1\. Reads the raw request body as bytes (do NOT parse and re-serialize the JSON before verifying)
2\. Checks that X-Timestamp is within 300 seconds of now
3\. Recomputes X-Signature-V2 = hex(HMAC-SHA256(secret, X-Timestamp + "." + rawBody)), where secret is the FULL signing secret including its "whsec_" prefix
4\. Compares the two signatures in constant time
5\. Parses the JSON, ignores an alert_id it already handled, answers 2xx within 5 seconds, then processes the alert asynchronously
6\. Answers a failed check with 400, not 401 (401, 403, 404 and 410 mark the destination for reconnection)

\## Headers
\- Content-Type: application/json
\- User-Agent: Didit-Alerts/1.0
\- X-Timestamp: Unix seconds, new on every attempt
\- X-Signature-V2: hex HMAC-SHA256 described above
This is NOT the signature scheme of verification webhooks. Alert webhooks send no X-Signature and no X-Signature-Simple.

\## Body (compact JSON, keys sorted)
\{
"alert_id": "alt_<16 hex characters>",  // the same on every retry: deduplicate on it
"created_at": 1791884460,                // Unix seconds when Didit created the alert
"data": { "event": "session_in_review", "label": "Session", "value": "#1042" },
"organization": { "id": "<uuid>", "name": "<your organization>" },
"severity": "info" | "warning" | "critical",
"subtitle": "<English explanation, can be empty>",
"title": "<English headline>",
"url": "<link to the Didit console>",
"webhook_type": "alert.<type>"
}

\## webhook_type values
\- alert.verification.in_review, alert.business_verification.in_review
\- alert.transaction.held, alert.aml.monitoring_hit
\- alert.billing.low_balance, alert.billing.autorefill_failed (data.paused), alert.billing.backup_card_charged
\- alert.webhook.delivery_failing
\- alert.service.degraded (data.status "degraded" or "down"), alert.service.recovered (data.status "recovered")
\- alert.test (sent from the console's Send test button)
Ignore webhook_type values and data keys you do not recognize.

\## Retries
\- 2xx = delivered. Redirects are not followed.
\- Timeout (5 s), connection failure, 5xx, 408, 425 or 429 = retried 5 times, after 1 min, 5 min, 30 min, 3 h and 20 h 24 min (about 24 h).
\- Any other response is final.
\- Test alerts are never retried.

\## Secret rotation
Rotating the secret in the console replaces it at once. There is no overlap and no header signed with the previous secret: deploy the new secret immediately.

Docs: /integration/alert-webhooks`

\</pre>



      
      

Colar em

Os webhooks de alertas enviam os alertas da sua equipe para um endpoint HTTPS que você controla.
Use-os para alimentar uma ferramenta de plantão, uma fila de tickets ou o seu próprio painel de operações.Os webhooks de alertas são separados dos [webhooks de verificação](https://docs.didit.me/pt-br/integration/webhooks.md).
Os webhooks de verificação enviam os resultados de sessões, entidades e transações para a sua aplicação.
Os webhooks de alertas avisam a sua equipe de que algo precisa de atenção: uma verificação aguardando revisão, um pagamento que falhou, um incidente no serviço.
Eles têm o próprio payload, segredo de assinatura, assinatura e cronograma de novas tentativas.## Configurar um destinoVocê configura os webhooks de alertas no Business Console, não pela API.
Os membros que podem gerenciar as configurações da organização podem adicionar, alterar e testar destinos.**1. Adicione o destino**Em **Configurações da organização** > **Notificações da equipe**, adicione um destino **Webhook de alertas** com a URL do seu endpoint.
A URL deve usar `https://` e ser resolvida para um endereço público.**2. Guarde o segredo de assinatura**A Didit mostra o segredo de assinatura completo, que começa com `whsec_`, apenas uma vez.
Guarde-o agora: depois disso, o console mostra apenas os quatro últimos caracteres.**3. Encaminhe alertas para ele**Adicionar um destino ainda não envia nada para ele.
Adicione o destino às regras de encaminhamento dos alertas que você quer que ele receba.**4. Envie um teste**Clique em **Enviar teste** para enviar um alerta `alert.test` e confira se o seu endpoint o aceita.Uma organização pode ter até 25 destinos em **Notificações da equipe**.Somente a atividade do ambiente de produção chega a um destino.
Alertas sobre verificações no sandbox, verificações de empresas, transações e ocorrências AML nunca são enviados a ele.## Tipos de alerta| `webhook_type` | `severity` | Quando a Didit o envia |
| - | - | - |
| `alert.verification.in_review` | `warning` | Uma verificação foi concluída e sinalizada para revisão manual. |
| `alert.business_verification.in_review` | `warning` | Uma verificação de empresa precisa de revisão manual. |
| `alert.transaction.held` | `warning` | As suas regras de monitoramento de transações sinalizaram uma transação para revisão. |
| `alert.aml.monitoring_hit` | `warning` | O monitoramento contínuo de AML encontrou uma nova ocorrência. |
| `alert.billing.low_balance` | `warning` | O saldo da sua organização ficou abaixo do limiar que você definiu. |
| `alert.billing.autorefill_failed` | `critical` | Uma recarga automática do seu saldo falhou porque o cartão foi recusado. |
| `alert.billing.backup_card_charged` | `warning` | Uma cobrança automática foi recusada em um cartão e paga com um cartão reserva. |
| `alert.webhook.delivery_failing` | `warning` | As entregas para um dos seus endpoints de [webhook de verificação](https://docs.didit.me/pt-br/integration/webhooks.md) estão falhando. |
| `alert.service.degraded` | `critical` | Um serviço que a sua organização usa está degradado ou fora do ar. |
| `alert.service.recovered` | `info` | Esse serviço voltou ao normal. |
| `alert.test` | `info` | Alguém clicou em **Enviar teste** no destino. |## PayloadTodo alerta é um `POST` com corpo JSON.
A Didit envia o corpo como JSON compacto, com as chaves em ordem alfabética.
Este exemplo está formatado para facilitar a leitura:```json theme={null}
{
  "alert_id": "alt_9f2c4e1a7b3d5c80",
  "created_at": 1791884460,
  "data": {
    "event": "session_in_review",
    "label": "Session",
    "value": "#1042"
  },
  "organization": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "name": "Example Org"
  },
  "severity": "warning",
  "subtitle": "A KYC or KYB verification was completed and flagged for manual review.",
  "title": "Verification in review",
  "url": "https://business.didit.me/console/3fa85f64-5717-4562-b3fc-2c963f66afa6/9b2d1c4e-8f3a-4e6b-a1d2-5c7e9f0b3a14/verifications?session-id=1b4e28ba-2fa1-41d2-883f-0016d3cca427",
  "webhook_type": "alert.verification.in_review"
}
```| Campo | Tipo | Descrição |
| - | - | - |
| `webhook_type` | string | `alert.` seguido do tipo de alerta. Consulte [Tipos de alerta](#tipos-de-alerta). |
| `alert_id` | string | `alt_` seguido de 16 caracteres hexadecimais. Toda nova tentativa de um alerta traz o mesmo `alert_id`, então use-o para ignorar duplicatas. |
| `created_at` | integer | Segundos Unix em que a Didit criou o alerta. Permanece o mesmo nas novas tentativas. |
| `severity` | string | `info`, `warning` ou `critical`. |
| `organization` | object | O `id` (UUID) e o `name` da sua organização. |
| `title` | string | Título curto em inglês. |
| `subtitle` | string | Explicação em inglês. Pode estar vazio. |
| `data` | object | O assunto do alerta. Veja abaixo. |
| `url` | string | Link para a página relacionada no console da Didit. |`data` sempre contém:- `event`: a chave do evento, por exemplo `session_in_review`.
- `label` e `value`: o assunto do alerta, por exemplo `Session` e `#1042`.Alguns tipos de alerta adicionam chaves a `data`:- `alert.billing.autorefill_failed` adiciona `paused`, um booleano que é `true` quando a recarga automática fica pausada até você atualizar o cartão.
- `alert.service.degraded` e `alert.service.recovered` adicionam `service`, `status`, `incident_id`, `service_name`, `state`, `affected`, `country_name`, `started_at`, `resolved_at`, `duration_minutes`, `title` e `body`.
  `status` é `degraded` ou `down` em `alert.service.degraded`, e `recovered` em `alert.service.recovered`.
  `service` usa as chaves de serviço de [`GET /v3/status/`](https://docs.didit.me/pt-br/api-reference/service-status), e `incident_id` corresponde a um `id` de incidente ali.Os alertas nunca incluem dados pessoais nem dados de documentos do usuário final.
Eles identificam uma sessão, transação ou outro item pelo número e apontam para ele no console.
Ignore valores de `webhook_type` e chaves de `data` que você não reconhecer, para que novos valores não quebrem o seu endpoint.## Cabeçalhos da requisição| Cabeçalho | Exemplo | Significado |
| - | - | - |
| `Content-Type` | `application/json` | O corpo é JSON. |
| `User-Agent` | `Didit-Alerts/1.0` | Identifica o remetente de alertas da Didit. |
| `X-Timestamp` | `1791884462` | Segundos Unix em que a Didit assinou esta tentativa. Muda a cada tentativa. |
| `X-Signature-V2` | 64 caracteres hexadecimais | Assinatura HMAC-SHA256 desta tentativa. |## Verificar a assinatura`X-Signature-V2` é o HMAC-SHA256 codificado em hexadecimal da string `<X-Timestamp>.<raw body>`, usando como chave o segredo de assinatura do seu destino.* Use o segredo completo exatamente como a Didit o mostrou, incluindo o prefixo `whsec_`.
* Assine os bytes brutos da requisição exatamente como você os recebeu. Fazer o parse do JSON e serializá-lo de novo pode alterar os bytes e invalidar a verificação.
* Compare as assinaturas em tempo constante.
* Rejeite uma requisição cujo `X-Timestamp` esteja a mais de 5 minutos do seu relógio. A Didit assina cada tentativa, incluindo cada nova tentativa, com um timestamp novo, então essa janela nunca rejeita uma nova tentativa legítima.
* Responda com `400` se a verificação falhar. Uma resposta `401`, `403`, `404` ou `410` marca o destino para reconexão (veja [Novas tentativas](#novas-tentativas)).> [!WARNING]
>
> Os webhooks de alerta não são assinados como os webhooks de verificação.
> O cabeçalho também se chama `X-Signature-V2`, mas a string assinada é diferente, e os webhooks de alerta não enviam `X-Signature` nem `X-Signature-Simple`.
> Não reutilize o verificador dos seus webhooks de verificação.### Node.js (Express)```javascript theme={null}
const express = require("express");
const crypto = require("crypto");

const app = express();
// The full secret Didit showed when you added the destination, including "whsec_".
const ALERT_SECRET = process.env.DIDIT_ALERT_SECRET;
const TOLERANCE_SECONDS = 300;

// express.raw keeps the body as the exact bytes Didit sent.
app.post("/api/didit/alerts", express.raw({ type: "application/json" }), (req, res) => {
  const timestamp = req.get("X-Timestamp") || "";
  const signature = Buffer.from(req.get("X-Signature-V2") || "", "utf8");

  const now = Math.floor(Date.now() / 1000);
  if (!/^\d+$/.test(timestamp) || Math.abs(now - Number(timestamp)) > TOLERANCE_SECONDS) {
    return res.status(400).send("Stale or missing timestamp");
  }

  const expected = Buffer.from(
    crypto.createHmac("sha256", ALERT_SECRET).update(`${timestamp}.`).update(req.body).digest("hex"),
    "utf8",
  );
  if (signature.length !== expected.length || !crypto.timingSafeEqual(signature, expected)) {
    return res.status(400).send("Invalid signature");
  }

  const alert = JSON.parse(req.body.toString("utf8"));
  res.sendStatus(204); // answer within 5 seconds, then process
  handleAlert(alert); // skip an alert_id you already handled
});
```### Python (Flask)```python theme={null}
import hashlib
import hmac
import json
import os
import time

from flask import Flask, request

app = Flask(__name__)
# The full secret Didit showed when you added the destination, including "whsec_".
ALERT_SECRET = os.environ["DIDIT_ALERT_SECRET"].encode("utf-8")
TOLERANCE_SECONDS = 300


@app.post("/api/didit/alerts")
def didit_alert():
    raw_body = request.get_data()  # the exact bytes Didit sent
    timestamp = request.headers.get("X-Timestamp", "")
    signature = request.headers.get("X-Signature-V2", "")

    try:
        too_old = abs(time.time() - int(timestamp)) > TOLERANCE_SECONDS
    except ValueError:
        too_old = True
    if too_old:
        return "Stale or missing timestamp", 400

    expected = hmac.new(ALERT_SECRET, timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected.encode(), signature.encode()):
        return "Invalid signature", 400

    alert = json.loads(raw_body)
    handle_alert(alert)  # skip an alert_id you already handled; keep this fast
    return "", 204
```### Rotacionar o segredo de assinaturaVocê rotaciona o segredo do destino em **Team notifications**.
A Didit mostra o novo segredo uma única vez e assina com ele todas as tentativas seguintes, incluindo as novas tentativas de alertas criados antes da rotação.
O segredo antigo deixa de funcionar imediatamente.
Não há período de sobreposição, e nenhum cabeçalho traz uma assinatura feita com o segredo anterior.
Implante o novo segredo no seu endpoint assim que fizer a rotação.## Novas tentativasA Didit considera entregue qualquer resposta `2xx`.
Cada tentativa espera até 5 segundos pela sua resposta e não segue redirecionamentos.A Didit tenta novamente enviar um alerta após um timeout, uma falha de conexão, uma resposta `5xx` ou uma resposta `408`, `425` ou `429`.
Ela tenta novamente até 5 vezes, ao longo de cerca de 24 horas:| Nova tentativa | Intervalo após a tentativa anterior |
| - | - |
| 1ª | 1 minuto |
| 2ª | 5 minutos |
| 3ª | 30 minutos |
| 4ª | 3 horas |
| 5ª | 20 horas e 24 minutos |Qualquer outra resposta é definitiva, incluindo `3xx` e qualquer outro `4xx`.
Um teste enviado com **Send test** é enviado uma única vez e nunca é reenviado.Uma resposta `401`, `403`, `404` ou `410` também marca o destino para reconexão: o console o exibe com o status **Reconnect**.
O mesmo acontece com uma URL que, no momento do envio pela Didit, é resolvida para um endereço privado ou bloqueado por outro motivo.
A Didit continua enviando novos alertas ao destino, e ele volta a ficar ativo após a próxima entrega bem-sucedida ou quando você atualiza a URL dele.**Recent notifications**, em **Team notifications**, lista cada entrega com o resultado, e você pode tentar novamente uma entrega que falhou a partir dali.## Relacionados- [Webhooks de verificação](https://docs.didit.me/pt-br/integration/webhooks.md): resultados de sessões, entidades e transações da sua aplicação.
- [Status do serviço](https://docs.didit.me/pt-br/api-reference/service-status.md): o feed público por trás de `alert.service.degraded` e `alert.service.recovered`.
- [Revisão manual](https://docs.didit.me/pt-br/console/manual-review.md): o que fazer com uma verificação em revisão.


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