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

# Alert webhook: notifiche del team verso il tuo endpoint

> Didit invia gli alert del team al tuo endpoint HTTPS: revisioni, hit AML, fatture, webhook falliti. Firmati HMAC-SHA256, ritentati per 24 ore.



Prompt webhook degli avvisi
        Copia il prompt negli appunti



      
      

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



      
      

Incolla in

I webhook degli avvisi inviano gli alert del tuo team a un endpoint HTTPS di tua proprietà.
Usali per alimentare uno strumento di reperibilità, una coda di ticket o la tua dashboard operativa.I webhook degli avvisi sono separati dai [webhook di verifica](https://docs.didit.me/it/integration/webhooks.md).
I webhook di verifica inviano alla tua applicazione i risultati di sessioni, entità e transazioni.
I webhook degli avvisi segnalano al tuo team che qualcosa richiede attenzione: una verifica in attesa di revisione, un pagamento non riuscito, un incidente di servizio.
Hanno un payload, un segreto di firma, una firma e una pianificazione dei nuovi tentativi propri.## Configurare una destinazioneConfiguri i webhook degli avvisi nella Business Console, non tramite API.
I membri che possono gestire le impostazioni dell'organizzazione possono aggiungere, modificare e testare le destinazioni.**1. Aggiungi la destinazione**In **Impostazioni dell'organizzazione** > **Notifiche del team**, aggiungi una destinazione **Webhook degli avvisi** con l'URL del tuo endpoint.
L'URL deve usare `https://` e puntare a un indirizzo pubblico.**2. Conserva il segreto di firma**Didit mostra il segreto di firma completo, che inizia con `whsec_`, una sola volta.
Conservalo subito: in seguito la console mostra solo i suoi ultimi quattro caratteri.**3. Instrada gli alert verso di essa**L'aggiunta di una destinazione non invia ancora nulla.
Aggiungi la destinazione alle regole di instradamento degli alert che vuoi che riceva.**4. Invia un test**Fai clic su **Invia test** per inviare un alert `alert.test` e verifica che il tuo endpoint lo accetti.Un'organizzazione può avere fino a 25 destinazioni in **Notifiche del team**.Solo le attività nell'ambiente di produzione raggiungono una destinazione.
Gli alert relativi a verifiche in sandbox, verifiche aziendali (KYB), transazioni e riscontri AML non vengono mai inviati a essa.## Tipi di alert| `webhook_type` | `severity` | Quando Didit lo invia |
| - | - | - |
| `alert.verification.in_review` | `warning` | Una verifica è stata completata e segnalata per la revisione manuale. |
| `alert.business_verification.in_review` | `warning` | Una verifica aziendale richiede una revisione manuale. |
| `alert.transaction.held` | `warning` | Le tue regole di monitoraggio delle transazioni hanno segnalato una transazione per la revisione. |
| `alert.aml.monitoring_hit` | `warning` | Il monitoraggio AML continuo ha trovato un nuovo riscontro. |
| `alert.billing.low_balance` | `warning` | Il saldo della tua organizzazione è sceso sotto la soglia che hai impostato. |
| `alert.billing.autorefill_failed` | `critical` | Una ricarica automatica del saldo non è riuscita perché la carta è stata rifiutata. |
| `alert.billing.backup_card_charged` | `warning` | Un addebito automatico è stato rifiutato su una carta e pagato con una carta di riserva. |
| `alert.webhook.delivery_failing` | `warning` | Le consegne a uno dei tuoi endpoint di [webhook di verifica](https://docs.didit.me/it/integration/webhooks.md) stanno fallendo. |
| `alert.service.degraded` | `critical` | Un servizio che la tua organizzazione utilizza è degradato o non disponibile. |
| `alert.service.recovered` | `info` | Quel servizio è tornato alla normalità. |
| `alert.test` | `info` | Qualcuno ha fatto clic su **Invia test** per la destinazione. |## PayloadOgni alert è una richiesta `POST` con un corpo JSON.
Didit invia il corpo come JSON compatto con le chiavi ordinate.
Questo esempio è formattato per la lettura:```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 | Descrizione |
| - | - | - |
| `webhook_type` | stringa | `alert.` seguito dal tipo di alert. Vedi [Tipi di alert](#tipi-di-alert). |
| `alert_id` | stringa | `alt_` seguito da 16 caratteri esadecimali. Ogni nuovo tentativo di un alert riporta lo stesso `alert_id`, quindi usalo per ignorare i duplicati. |
| `created_at` | intero | Secondi Unix in cui Didit ha creato l'alert. Resta invariato nei nuovi tentativi. |
| `severity` | stringa | `info`, `warning` o `critical`. |
| `organization` | oggetto | `id` (UUID) e `name` della tua organizzazione. |
| `title` | stringa | Titolo breve in inglese. |
| `subtitle` | stringa | Spiegazione in inglese. Può essere vuota. |
| `data` | oggetto | Ciò a cui si riferisce l'alert. Vedi sotto. |
| `url` | stringa | Link alla pagina correlata nella console di Didit. |`data` contiene sempre:- `event`: la chiave dell'evento, per esempio `session_in_review`.
- `label` e `value`: a cosa si riferisce l'alert, per esempio `Session` e `#1042`.Alcuni tipi di alert aggiungono chiavi a `data`:- `alert.billing.autorefill_failed` aggiunge `paused`, un booleano che vale `true` quando la ricarica automatica è sospesa finché non aggiorni la carta.
- `alert.service.degraded` e `alert.service.recovered` aggiungono `service`, `status`, `incident_id`, `service_name`, `state`, `affected`, `country_name`, `started_at`, `resolved_at`, `duration_minutes`, `title` e `body`.
  `status` vale `degraded` o `down` in `alert.service.degraded`, e `recovered` in `alert.service.recovered`.
  `service` usa le chiavi di servizio di [`GET /v3/status/`](https://docs.didit.me/it/api-reference/service-status), e `incident_id` corrisponde a un `id` di incidente in quella risposta.Gli alert non includono mai dati personali degli utenti finali o dati dei loro documenti.
Identificano una sessione, una transazione o un altro elemento tramite il suo numero e rimandano a esso nella console.
Ignora i valori di `webhook_type` e le chiavi di `data` che non riconosci, così le nuove non interrompono il tuo endpoint.## Intestazioni della richiesta| Intestazione | Esempio | Significato |
| - | - | - |
| `Content-Type` | `application/json` | Il corpo è in JSON. |
| `User-Agent` | `Didit-Alerts/1.0` | Identifica il mittente di alert di Didit. |
| `X-Timestamp` | `1791884462` | Secondi Unix in cui Didit ha firmato questo tentativo. Nuovo a ogni tentativo. |
| `X-Signature-V2` | 64 caratteri esadecimali | Firma HMAC-SHA256 di questo tentativo. |## Verifica la firma`X-Signature-V2` è l'HMAC-SHA256 codificato in esadecimale della stringa `<X-Timestamp>.<raw body>`, utilizzando come chiave il segreto di firma della tua destinazione.* Usa il segreto completo esattamente come te lo ha mostrato Didit, incluso il prefisso `whsec_`.
* Firma i byte grezzi della richiesta così come li hai ricevuti. Analizzare il JSON e serializzarlo di nuovo può modificare i byte e far fallire il controllo.
* Confronta le firme in tempo costante.
* Rifiuta una richiesta il cui `X-Timestamp` si discosta di più di 5 minuti dal tuo orologio. Didit firma ogni tentativo, inclusi i singoli nuovi tentativi, con un timestamp nuovo, quindi questa finestra non rifiuta mai un nuovo tentativo legittimo.
* Rispondi a un controllo fallito con `400`. Una risposta `401`, `403`, `404` o `410` contrassegna la destinazione per la riconnessione (vedi [Nuovi tentativi](#nuovi-tentativi)).> [!WARNING]
>
> I webhook degli alert non sono firmati come i webhook di verifica.
> L'intestazione si chiama anch'essa `X-Signature-V2`, ma la stringa firmata è diversa, e i webhook degli alert non inviano né `X-Signature` né `X-Signature-Simple`.
> Non riutilizzare il verificatore del tuo webhook di verifica.### 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
```### Ruota il segreto di firmaRuoti il segreto sulla destinazione in **Notifiche del team**.
Didit mostra il nuovo segreto una sola volta e firma con esso ogni tentativo successivo, inclusi i nuovi tentativi degli alert creati prima della rotazione.
Il vecchio segreto smette di funzionare immediatamente.
Non c'è alcun periodo di sovrapposizione e nessuna intestazione contiene una firma creata con il segreto precedente.
Distribuisci il nuovo segreto sul tuo endpoint non appena lo ruoti.## Nuovi tentativiDidit considera consegnata qualsiasi risposta `2xx`.
Ogni tentativo attende fino a 5 secondi la tua risposta e non segue i reindirizzamenti.Didit effettua un nuovo tentativo di invio dell'alert dopo un timeout, un errore di connessione, una risposta `5xx`, oppure una risposta `408`, `425` o `429`.
Effettua fino a 5 nuovi tentativi, nell'arco di circa 24 ore:| Tentativo | Ritardo dopo il tentativo precedente |
| - | - |
| 1° | 1 minuto |
| 2° | 5 minuti |
| 3° | 30 minuti |
| 4° | 3 ore |
| 5° | 20 ore e 24 minuti |Ogni altra risposta è definitiva, inclusa qualsiasi risposta `3xx` e qualsiasi altra risposta `4xx`.
Per un test inviato con **Invia test** viene effettuato un solo tentativo e non viene mai ritentato.Una risposta `401`, `403`, `404` o `410` contrassegna anche la destinazione per la riconnessione: la console la mostra con lo stato **Ricollega**.
Lo stesso vale per un URL che si risolve in un indirizzo privato o altrimenti bloccato al momento dell'invio da parte di Didit.
Didit continua a inviare i nuovi alert alla destinazione e questa torna attiva alla consegna riuscita successiva o quando aggiorni il suo URL.**Notifiche recenti** in **Notifiche del team** elencano ogni consegna con il suo esito, e da lì puoi ritentare una consegna non riuscita.## Correlati- [Webhook di verifica](https://docs.didit.me/it/integration/webhooks.md): risultati di sessione, entità e transazione per la tua applicazione.
- [Stato del servizio](https://docs.didit.me/it/api-reference/service-status.md): il feed pubblico alla base di `alert.service.degraded` e `alert.service.recovered`.
- [Revisione manuale](https://docs.didit.me/it/console/manual-review.md): cosa fare con una verifica in revisione.


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