> ## 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 d'alerte : alertes d'équipe vers votre endpoint

> Recevez les alertes Didit sur votre endpoint HTTPS : examens, surveillance AML, facturation. Signées HMAC-SHA256, réessayées pendant 24 heures.



Prompt pour le webhook d'alertes
        Copier le prompt dans le presse-papiers



      
      

\<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>



      
      

Coller dans

Les webhooks d'alertes envoient les alertes de votre équipe vers un endpoint HTTPS que vous contrôlez.
Utilisez-les pour alimenter un outil d'astreinte, une file d'attente de tickets ou votre propre tableau de bord des opérations.Les webhooks d'alertes sont distincts des [webhooks de vérification](https://docs.didit.me/fr/integration/webhooks.md).
Les webhooks de vérification envoient les résultats de session, d'entité et de transaction à votre application.
Les webhooks d'alertes préviennent votre équipe qu'une situation nécessite son attention : une vérification en attente d'examen, un échec de paiement, un incident de service.
Ils ont leur propre payload, secret de signature, signature et calendrier de réessais.## Configurer une destinationVous configurez les webhooks d'alertes dans la Business Console, et non via l'API.
Les membres qui peuvent gérer les paramètres de l'organisation peuvent ajouter, modifier et tester des destinations.**1. Ajouter la destination**Dans **Paramètres de l'organisation** > **Notifications de l'équipe**, ajoutez une destination **Webhook d'alertes** avec l'URL de votre endpoint.
L'URL doit utiliser `https://` et pointer vers une adresse publique.**2. Conserver le secret de signature**Didit n'affiche le secret de signature complet, commençant par `whsec_`, qu'une seule fois.
Enregistrez-le dès maintenant : la console n'affichera ensuite que ses quatre derniers caractères.**3. Y acheminer les alertes**L'ajout d'une destination n'envoie encore rien vers celle-ci.
Ajoutez la destination aux règles de routage des alertes que vous souhaitez lui faire recevoir.**4. Envoyer un test**Cliquez sur **Envoyer un test** pour envoyer une alerte `alert.test`, et vérifiez que votre endpoint l'accepte.Une organisation peut avoir jusqu'à 25 destinations dans **Notifications de l'équipe**.Seule l'activité en production parvient à une destination.
Les alertes concernant les vérifications sandbox, les vérifications d'entreprise, les transactions et les correspondances AML ne lui sont jamais envoyées.## Types d'alertes| `webhook_type` | `severity` | Quand Didit l'envoie |
| - | - | - |
| `alert.verification.in_review` | `warning` | Une vérification a été effectuée et signalée pour examen manuel. |
| `alert.business_verification.in_review` | `warning` | Une vérification d'entreprise nécessite un examen manuel. |
| `alert.transaction.held` | `warning` | Vos règles de surveillance des transactions ont signalé une transaction pour examen. |
| `alert.aml.monitoring_hit` | `warning` | La surveillance AML continue a détecté une nouvelle correspondance. |
| `alert.billing.low_balance` | `warning` | Le solde de votre organisation est tombé en dessous du seuil que vous avez défini. |
| `alert.billing.autorefill_failed` | `critical` | Une recharge automatique de votre solde a échoué car la carte a été refusée. |
| `alert.billing.backup_card_charged` | `warning` | Un débit automatique a été refusé sur une carte et réglé avec une carte de secours. |
| `alert.webhook.delivery_failing` | `warning` | Les livraisons vers l'un de vos endpoints de [webhook de vérification](https://docs.didit.me/fr/integration/webhooks.md) échouent. |
| `alert.service.degraded` | `critical` | Un service utilisé par votre organisation est dégradé ou interrompu. |
| `alert.service.recovered` | `info` | Ce service fonctionne de nouveau normalement. |
| `alert.test` | `info` | Quelqu'un a cliqué sur **Envoyer un test** pour cette destination. |## PayloadChaque alerte est un `POST` avec un corps en JSON.
Didit envoie le corps sous forme de JSON compact avec ses clés triées.
Cet exemple est mis en forme pour en faciliter la lecture :```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"
}
```| Champ | Type | Description |
| - | - | - |
| `webhook_type` | string | `alert.` suivi du type d'alerte. Voir [Types d'alertes](#types-d’alertes). |
| `alert_id` | string | `alt_` suivi de 16 caractères hexadécimaux. Chaque nouvelle tentative d'envoi d'une alerte porte le même `alert_id`, utilisez-le donc pour ignorer les doublons. |
| `created_at` | integer | Secondes Unix au moment où Didit a créé l'alerte. Il reste identique lors des réessais. |
| `severity` | string | `info`, `warning` ou `critical`. |
| `organization` | object | L'`id` (UUID) et le `name` de votre organisation. |
| `title` | string | Titre court en anglais. |
| `subtitle` | string | Explication en anglais. Peut être vide. |
| `data` | object | Objet de l'alerte. Voir ci-dessous. |
| `url` | string | Lien vers la page associée dans la console Didit. |`data` contient toujours :- `event` : la clé de l'événement, par exemple `session_in_review`.
- `label` et `value` : l'objet de l'alerte, par exemple `Session` et `#1042`.Certains types d'alertes ajoutent des clés à `data` :- `alert.billing.autorefill_failed` ajoute `paused`, un booléen valant `true` lorsque la recharge automatique est suspendue jusqu'à ce que vous mettiez à jour la carte.
- `alert.service.degraded` et `alert.service.recovered` ajoutent `service`, `status`, `incident_id`, `service_name`, `state`, `affected`, `country_name`, `started_at`, `resolved_at`, `duration_minutes`, `title` et `body`.
  `status` vaut `degraded` ou `down` pour `alert.service.degraded`, et `recovered` pour `alert.service.recovered`.
  `service` utilise les clés de service de [`GET /v3/status/`](https://docs.didit.me/fr/api-reference/service-status), et `incident_id` correspond à un `id` d'incident sur cet endpoint.Les alertes ne contiennent jamais de données personnelles d'utilisateurs finaux ou de données de documents.
Elles identifient une session, une transaction ou tout autre élément par son numéro et contiennent un lien vers celui-ci dans la console.
Ignorez les valeurs de `webhook_type` et les clés de `data` que vous ne reconnaissez pas, afin que les nouveaux éléments n'interrompent pas votre endpoint.## En-têtes de requête| En-tête | Exemple | Signification |
| - | - | - |
| `Content-Type` | `application/json` | Le corps est en JSON. |
| `User-Agent` | `Didit-Alerts/1.0` | Identifie l'expéditeur d'alertes de Didit. |
| `X-Timestamp` | `1791884462` | Secondes Unix au moment où Didit a signé cette tentative. Mis à jour à chaque tentative. |
| `X-Signature-V2` | 64 caractères hexadécimaux | Signature HMAC-SHA256 de cette tentative. |## Vérifier la signature`X-Signature-V2` est le HMAC-SHA256 encodé en hexadécimal de la chaîne `<X-Timestamp>.<raw body>`, calculé avec le secret de signature de votre destination.* Utilisez le secret complet exactement tel que Didit l'a affiché, y compris le préfixe `whsec_`.
* Signez les octets bruts de la requête tels que vous les avez reçus. Parser le JSON et le sérialiser à nouveau peut modifier les octets et faire échouer la vérification.
* Comparez les signatures en temps constant.
* Rejetez toute requête dont le `X-Timestamp` présente un écart de plus de 5 minutes avec votre horloge. Didit signe chaque tentative, y compris chaque nouvel essai, avec un horodatage récent, de sorte que cette fenêtre ne rejette jamais une nouvelle tentative légitime.
* Répondez à un échec de vérification avec le code `400`. Une réponse `401`, `403`, `404` ou `410` marque la destination comme nécessitant une reconnexion (voir [Nouvelles tentatives](#nouvelles-tentatives)).> [!WARNING]
>
> Les webhooks d'alerte ne sont pas signés de la même manière que les webhooks de vérification.
> L'en-tête s'appelle également `X-Signature-V2`, mais la chaîne signée est différente, et les webhooks d'alerte n'envoient ni `X-Signature` ni `X-Signature-Simple`.
> Ne réutilisez pas votre vérificateur de webhooks de vérification.### 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
```### Renouveler le secret de signatureVous renouvelez le secret sur la destination dans **Notifications de l’équipe**.
Didit affiche le nouveau secret une seule fois et signe toutes les tentatives ultérieures avec celui-ci, y compris les nouvelles tentatives d'alertes créées avant le renouvellement.
L'ancien secret cesse immédiatement de fonctionner.
Il n'y a pas de période de chevauchement, et aucun en-tête ne porte de signature générée avec le secret précédent.
Déployez le nouveau secret sur votre endpoint dès que vous le renouvelez.## Nouvelles tentativesDidit considère toute réponse `2xx` comme livrée.
Didit attend votre réponse jusqu'à 5 secondes pour chaque tentative et ne suit pas les redirections.Didit réessaie d'envoyer une alerte après l'expiration du délai d'attente, un échec de connexion, une réponse `5xx`, ou une réponse `408`, `425` ou `429`.
Le système effectue jusqu'à 5 nouvelles tentatives, réparties sur environ 24 heures :| Nouvelle tentative | Délai après la tentative précédente |
| - | - |
| 1re | 1 minute |
| 2e | 5 minutes |
| 3e | 30 minutes |
| 4e | 3 heures |
| 5e | 20 heures 24 minutes |Toute autre réponse est définitive, y compris les codes `3xx` et tout autre code `4xx`.
Un test envoyé avec **Envoyer un test** fait l'objet d'une seule tentative et n'est jamais réessayé.Une réponse `401`, `403`, `404` ou `410` marque également la destination comme nécessitant une reconnexion : la console l'affiche avec l'état **Reconnecter**.
Il en va de même pour une URL qui renvoie vers une adresse privée ou autrement bloquée au moment de l'envoi par Didit.
Didit continue d'envoyer de nouvelles alertes à la destination, et celle-ci redevient active après la prochaine livraison réussie ou lorsque vous mettez à jour son URL.La section **Notifications récentes** dans **Notifications de l’équipe** liste chaque livraison avec son résultat, et vous pouvez relancer une livraison ayant échoué à partir de là.## Voir aussi- [Webhooks de vérification](https://docs.didit.me/fr/integration/webhooks.md) : résultats de session, d'entité et de transaction pour votre application.
- [État du service](https://docs.didit.me/fr/api-reference/service-status.md) : le flux public associé à `alert.service.degraded` et `alert.service.recovered`.
- [Examen manuel](https://docs.didit.me/fr/console/manual-review.md) : que faire lorsqu'une vérification est en cours d'examen.


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