Zum Hauptinhalt springen
Authentifizierungsaktivität und ihre versionsabhängige Webhook-Zustellung sind in Authentifizierungsereignisse und Webhooks dokumentiert. Registrieren Sie keine nicht aufgeführten Auth-Ereignistypen über die aktuelle Ziel-API.

Übersicht

Über Webhooks sendet Didit Echtzeit-Updates an Ihr Backend, sobald eine Verifizierungssitzung, Unternehmenssitzung, ein Vendor-Benutzer, ein Vendor-Unternehmen oder eine Transaktion den Status ändert. Wenn Sie ein oder mehrere Ziele für die Ereignisse abonnieren, die für Sie relevant sind, können Sie sofort reagieren, sobald ein Ergebnis vorliegt – meist innerhalb von Sekunden, nachdem der zu verifizierende Benutzer seinen Ablauf abgeschlossen hat. Webhooks sind das empfohlene Integrationsmuster. Das Abfragen (Polling) von GET /v3/session/{id}/decision/ wird als Fallback unterstützt, ist jedoch langsamer, verbraucht mehr Anfragen und erfasst keine Ereignisse wie data.updated, transaction.status.updated sowie Änderungen auf Entitätsebene, die ausschließlich über Webhooks gesendet werden. Behandeln Sie die Webhook-Zustellung als primäre Datenquelle (Quelle der Wahrheit) und nutzen Sie Polling nur beim Kaltstart oder zum Datenabgleich. Dieses Kapitel der Didit Academy-Integrationslektion schließt an die Webhook-Übersicht an. Die Integrationslektion der Academy behandelt Webhooks ab 3:12: Hinzufügen eines Ziels, Auswählen von Ereignissen und das Signaturgeheimnis, das Sie weiter unten verifizieren.
Cloudflare-/WAF-Benutzer. Didit stellt Webhooks von der statischen Adresse 18.203.201.92 mit dem Header User-Agent: DiditWebhook/2.0 +https://didit.me zu. Wenn Ihre Edge-Infrastruktur unbekannte Clients blockiert, lassen Sie diese Adresse unter Security > WAF > Tools > IP Access Rules zu und wählen Sie Zulassen für den empfangenden Hostnamen aus.

Ereignistypen

Verwenden Sie den genauen Wert im subscribed_events-Array eines Webhook-Ziels. Es gibt keine Wildcards — führen Sie jede Familie auf, die Sie empfangen möchten — und Sie können Ereignisse auf mehrere Webhook-Ziele verteilen. Die Werte in dieser Tabelle stellen die vollständige Menge der abonnierbaren Ereignisse dar – es gibt kein session.status.updated, kyc.completed oder Ähnliches. Die einzige andere Zustellung, die Didit vornimmt, ist der unsignierte kyb.registry_search.resolved-Callback, der an die anfragespezifische webhook_url gesendet wird, die Sie an POST /v3/kyb/search/ übergeben, niemals an ein Webhook-Ziel.
Das Menü Business Console > API & Webhooks > Try Webhook sendet vollständige Payloads für jedes dieser Ereignisse (genehmigt, abgelehnt, in Prüfung, Unicode-Namen, KYC/KYB, Entität, Aktivität und Transaktion). Nutzen Sie es, um Ihren Endpoint zu validieren, bevor Sie live gehen.

Payload-Struktur

Envelope

Jeder Webhook nutzt denselben Envelope. Felder werden immer mit sort_keys=True und kompakten Trennzeichen (, / :) serialisiert, sodass dieselbe Payload auf beiden Seiten dieselbe Signatur erzeugt. Bei Sitzungs-Ereignissen (status.updated, data.updated bei regulären und Unternehmenssitzungen) enthält der Envelope zusätzlich:

Beispiel für Session-Envelope

Das decision-Objekt hat dasselbe Schema, das von GET /v3/session/{id}/decision/ zurückgegeben wird. Die kanonische Feldreferenz pro Funktion (jede Eigenschaft von id_verifications[], aml_screenings[] usw.) finden Sie unter Datenmodelle. Wir duplizieren die Feldtabellen pro Funktion hier bewusst nicht, damit das Schema nur an einer einzigen Stelle gepflegt wird.

Plural-Arrays in V3 (wichtig)

Innerhalb von decision ist jedes funktionsbezogene Ergebnis ein Plural-Array, sodass ein einzelner Workflow mehrere Instanzen derselben Funktion enthalten kann (beispielsweise mehrere Ausweisprüfungen über verschiedene Dokumente hinweg):
Jedes Element enthält eine node_id, die dem Workflow-Graphen entspricht, sowie einen funktionsbezogenen status. Lesen Sie die veralteten V2-Singular-Felder (id_verification, nfc, liveness, face_match, phone, email, poa, aml, ip_analysis, database_validation) nicht aus; sie erscheinen nur, wenn ein Ziel explizit auf webhook_version: "v2" festgelegt ist. Ergebnisse der E-Signatur befinden sich in signatures[] und sind nur vorhanden, wenn der Workflow einen E-Signatur-Schritt enthält: ein Element pro Unterzeichner, mit dem Unterzeichner, der Code-Bestätigung, dem Zertifikat, dem Zeitstempel, den Hashes und Download-Pfaden jedes Dokuments sowie dem Nachweis-PDF. Es gibt kein separates Signaturereignis: status.updated teilt Ihnen mit, wann Sie sie auslesen können. Siehe den E-Signatur-Bericht. Ergebnisse der Bankverifizierung befinden sich in bank_verifications[], mit einem Element pro Bankverifizierungsschritt. Das Feld method jedes Elements gibt an, wie das Konto verifiziert wurde: open_banking (Bank-Login), account_lookup (Kontoabfrage) oder ein Fallback über statement oder microdeposits, sowie null, bis ein Konto ausgewertet wurde. Wenn die Person Continue without your bank ausgewählt hat, ist continued_without_bank true und continuation gibt den Grund an. method ist dann none, es sei denn, ein Versuch wurde zuvor ausgewertet: siehe Wenn die Person ihre Bank nicht verifizieren kann. Bei einem über die Kontoabfrage verifizierten Konto werden keine Finanzdaten ausgelesen, weshalb financial_data.not_collected die Kontostände, Transaktionen oder Einkünfte auflistet, die der Schritt angefordert hat. Siehe Methoden der Bankverifizierung.

Investigator-Block

Eine decision der Version V3 für eine Benutzer- oder Unternehmenssitzung enthält zudem investigator: die neueste Investigator-Prüfung der Sitzung oder null, falls es nie eine gab. Funktionen zur Dokumentenerfassung – KYB-Dokumente und Document AI – verfügen über kein eigenes Plural-Array; sie erscheinen in der features[]-Liste der Entscheidung als { "feature": "DOCUMENT_AI", "node_id": "…" } (ein Eintrag pro Knoten). Siehe Document AI. Entscheidungen der Benutzerverifizierung enthalten zudem ein einzelnes biometric_consent-Objekt: ob die Sitzung eine ausdrückliche biometrische Einwilligung erforderte, den trigger, den status der Antwort und deren Nachweis. status: "pending" mit reason: "BIOMETRIC_CONSENT_UNSUPPORTED_SDK" bedeutet, dass ein natives SDK unter 4.10.0 ein Gesicht ohne den Einwilligungsschritt erfasst hat. Siehe Biometrische Einwilligung.

Schlüssel für Ausweisverifizierungsmethoden

Jedes Element in id_verifications[] gibt an, welche Ausweisverifizierungsmethode es erzeugt hat, sodass ein Handler anhand der tatsächlich erhaltenen Nachweise verzweigen kann: Bestehende Handler funktionieren unverändert weiter: Eine Dokumentsitzung setzt verification_method auf document und belässt den Rest bei null. Typen für jedes einzelne Feld finden Sie unter Datenmodelle.

Genehmigte Wallet-Sitzung (status.updated)

Payloads von Entitätsereignissen

Entitätsstatus sind ACTIVE, FLAGGED oder BLOCKED. user.data.updated und business.data.updated behalten das status-Feld bei und fügen ein changed_fields-Array sowie eine changes-Map mit { field: { previous, current } } hinzu. business.*-Ereignisse verwenden vendor_business_id anstelle von vendor_user_id.

Transaktionsereignis-Payloads

Transaktions-Webhooks nutzen dieselbe Fan-out-Infrastruktur und dieselbe Envelope-Struktur, identifizieren jedoch die Transaktion anstelle einer Sitzung:
action_required ist null oder die ausstehende Benutzeraktion (type wallet_ownership oder verification_session mit der zugehörigen url), wie unter Erforderliche Benutzeraktionen beschrieben. Neue Transaktionen haben standardmäßig den Status APPROVED. Wenn eine Regel, eine Sperrliste oder ein Workflow den Status während der Erstellung ändert, spiegelt die anfängliche Payload von transaction.created diesen finalen Status wider. transaction.status.updated weist dieselbe Struktur mit dem aktualisierten status auf. Transaktionsstatus sind APPROVED, IN_REVIEW, DECLINED oder AWAITING_USER; severity ist UNKNOWN / LOW / MEDIUM / HIGH / CRITICAL und direction ist INBOUND / OUTBOUND. transaction.created und transaction.status.updated werden nur für Live-Anwendungen zugestellt. Austauschvorgänge nach der Travel Rule in der Sandbox geben travel_rule.status.updated mit environment: sandbox aus.

Callback für die KYB-Unternehmensregistersuche

Der asynchrone Ablauf POST /v3/kyb/search/ stellt eine zusätzliche Payload zu, die kein Zielereignis ist: Wenn Sie eine webhook_url an die Suchanfrage übergeben, sendet Didit per POST einen kyb.registry_search.resolved-Callback an diese URL, sobald die Registersuche aufgelöst wurde. Er unterscheidet sich in drei Punkten von Ziel-Webhooks:
  • Er ist nicht signiert – keine X-Signature*-Header. Didit kennzeichnet ihn stattdessen mit X-Didit-Unsigned-Callback: true; validieren Sie ihn, indem Sie die request_id mit der von der Suchanfrage zurückgegebenen ID abgleichen.
  • Der Ereignisname steht in event_type (nicht in webhook_type), und es gibt kein application_id oder environment.
  • timestamp und created_at sind Epoch-Ganzzahlen, wie bei anderen Webhooks, jedoch im Gegensatz zum ISO-Format von created_at des Such-Endpoints.
Der Callback folgt derselben Wiederholungsrichtlinie wie Ziel-Webhooks (bei Wiederholungsversuchen wird timestamp mit einem neuen Zeitstempel versehen, und die Nachrichten bleiben unsigniert). Das vollständige Schema der Kandidatenliste finden Sie in der KYB-Unternehmensregister-API.

Signaturprüfung

Jeder Webhook wird mittels HMAC-SHA256 unter Verwendung des secret_shared_key des Ziels signiert. Es werden drei Signatur-Header gesendet, damit Sie denjenigen auswählen können, der Ihren Stack unverändert durchläuft: Header, die Didit immer sendet: Für 24 Stunden nach der Rotation des Signaturgeheimnisses eines Ziels enthalten Zustellungen zusätzlich X-Signature-Previous, X-Signature-V2-Previous und X-Signature-Simple-Previous (siehe Rotation des Signaturgeheimnisses).

Rotation des Signaturgeheimnisses

Sie rotieren das Signaturgeheimnis eines Webhook-Ziels in der Business Console. Für 24 Stunden nach einer Rotation enthält jede Zustellung an dieses Ziel drei zusätzliche Header, die mit dem ersetzten Geheimnis erstellt wurden: Ein Handler, der eine Übereinstimmung mit einem Signatur-Header oder dessen -Previous-Header akzeptiert, führt die Verifizierung unterbrechungsfrei fort, während Sie das neue Geheimnis bereitstellen: Das alte Geheimnis stimmt mit dem -Previous-Header überein und das neue mit dem regulären Header. Die folgenden Codebeispiele setzen dies um. Stellen Sie das neue Geheimnis innerhalb der 24 Stunden bereit, da danach nur noch das neue Geheimnis verifiziert werden kann. previous_secret_expires_at unter GET /v3/webhook/destinations/{destination_uuid}/ gibt an, wann die 24 Stunden enden. Wiederholungsversuche und erneute Sendungen werden beim Versand erneut mit dem aktuellen Geheimnis des Ziels signiert und enthalten die -Previous-Header nur so lange, wie die 24 Stunden andauern. Eine erneute Rotation innerhalb der 24 Stunden setzt das älteste Geheimnis sofort außer Kraft: Nur das Geheimnis, das bis zur neuen Rotation aktuell war, verifiziert weiterhin über die -Previous-Header – für 24 Stunden ab der neuen Rotation. Die -Previous-Header existieren nur für Webhook-Ziele. Der auf Anwendungsebene definierte secret_shared_key, der die veraltete webhook_url signiert, bietet keine solche Übergangsphase: Eine Rotation über rotate_secret_key unter PATCH /v3/webhook/ macht das alte Geheimnis sofort ungültig.

X-Signature-Simple ist veraltet

X-Signature-Simple authentifiziert den Umschlag, nicht die Payload. Sein kanonischer String deckt nur timestamp, session_id, status und webhook_type ab. Er deckt decision nicht ab – ebenso wenig wie resubmit_info, metadata, vendor_data oder event_id. Eine Anfrage, deren decision manipuliert wurde, während diese vier Felder unberührt blieben, besteht die Prüfung mit X-Signature-Simple weiterhin.Bei Ereignissen, die überhaupt keine session_id enthalten, ist er noch schwächer. Transaktionsereignisse (transaction_id, txn_id, amount, score) und Entitätsereignisse (vendor_user_id, vendor_business_id) signieren "{timestamp}::{status}:{webhook_type}", was nicht identifiziert, um welche Transaktion oder Entität es sich handelt – zwei verschiedene Transaktionen, die in derselben Sekunde mit demselben Status gemeldet werden, besitzen dasselbe X-Signature-Simple. Ereignisse vom Typ travel_rule.status.updated, workflow.compliance_changed und transaction_rule_suggestions.created enthalten auch keinen status, daher signieren sie "{timestamp}:::{webhook_type}", beispielsweise "{timestamp}:::travel_rule.status.updated".Wenn X-Signature-Simple der einzige Header ist, den Sie prüfen, behandeln Sie alles außerhalb dieser vier Felder als nicht vertrauenswürdig und rufen Sie den Datensatz erneut über die API ab – GET /v3/session/{session_id}/decision/ für eine Sitzung, GET /v3/transactions/{transaction_id}/ für eine Transaktion –, welche die maßgebliche Datenquelle ist.
Dies ist eine Kompatibilitätsbeschränkung und kann nicht ohne Weiteres direkt geändert werden. Der aus vier Feldern bestehende kanonische String ist ein Wire-Contract: Jeder Empfänger, der X-Signature-Simple verifiziert, berechnet genau diese vier Felder neu. Eine Änderung dessen, was der Header signiert, würde daher bei allen Empfängern die Signaturprüfung unbemerkt fehlschlagen lassen. Die Abdeckung des Bodys erfordert zudem eine spezifizierte kanonische Form des JSON, die beide Seiten Byte für Byte reproduzieren, und Didit stellt eine solche bereits mit X-Signature-V2 bereit. Anstatt eine zweite Body-abdeckende Variante mit eigenen Kanonisierungsregeln einzuführen, bleibt der Header genau so, wie er ist, und wird zugunsten der bestehenden Varianten mit vollständiger Payload-Abdeckung als veraltet eingestuft. Was das für Sie bedeutet:
  • Bei der Übertragung ändert sich nichts. X-Signature-Simple wird weiterhin bei jeder Zustellung mit derselben Berechnungsmethode gesendet. Bestehende Prüfungen funktionieren weiterhin wie gewohnt, und wir werden eine eventuelle Entfernung rechtzeitig ankündigen.
  • Neue Integrationen müssen X-Signature-V2 verwenden. Es ist middleware-sicher und deckt die gesamte Payload ab.
  • Wenn Sie X-Signature-Simple nicht ablösen können, verifizieren Sie den Header und rufen Sie den Datensatz anschließend über die API ab, anstatt ihn aus dem Webhook-Body auszulesen.

Warum drei Varianten?

Einige Web-Frameworks (Express-Body-Parser, Django-Middleware, API-Gateways) codieren JSON stillschweigend neu, bevor Ihr Handler es liest. Wenn der ursprüngliche Body "José" enthielt und Ihre Middleware ihn in "José" umschreibt, ändern sich die Bytes, obwohl die Daten identisch sind. X-Signature schlägt dann fehl. X-Signature-V2 wird anhand einer kanonischen JSON-Form berechnet (sortierte Schlüssel, kompakte Trennzeichen, nicht-escapetes Unicode), die nahezu jede Middleware deterministisch reproduziert – und das veraltete X-Signature-Simple dient als Fallback, indem es nur einen kurzen, parser-unabhängigen String signiert, falls selbst das fehlschlägt. Da es nur diesen kurzen String und niemals den Body signiert, kann es die Integrität des Bodys nicht gewährleisten.

Empfohlene Prüfreihenfolge

  1. Versuchen Sie zuerst X-Signature-V2. Re-enkodieren Sie JSON.parse(body) mit sortierten Schlüsseln und beibehaltenem Unicode, führen Sie dann HMAC-SHA256(secret, canonical) aus und vergleichen Sie das Ergebnis mittels timingSafeEqual mit dem Header.
  2. Wenn V2 fehlschlägt und Sie rohe Bytes lesen können, versuchen Sie X-Signature gegen HMAC-SHA256(secret, rawBody).
  3. Nur wenn beides fehlschlägt, greifen Sie auf das veraltete X-Signature-Simple gegen HMAC-SHA256(secret, "{timestamp}:{session_id}:{status}:{webhook_type}") zurück. Dies authentifiziert ausschließlich den Umschlag: Behandeln Sie die decision im Body als nicht vertrauenswürdig und rufen Sie sie erneut über GET /v3/session/{session_id}/decision/ ab, bevor Sie darauf reagieren.
Akzeptieren Sie bei jedem Schritt, wenn der entsprechende -Previous-Header vorhanden ist, eine Übereinstimmung mit einem der beiden Header (siehe Rotation des Signatur-Secrets). Lehnen Sie in jedem Fall alle Anfragen ab, die älter als 5 Minuten sind (abs(now - X-Timestamp) > 300), um sich vor Replay-Angriffen zu schützen.

Wiederholungsrichtlinie

Wenn Ihr Endpoint mit einem 5xx oder 404 antwortet, unternimmt Didit bis zu 2 Wiederholungsversuche mit exponentiellem Backoff:
  • 1. Wiederholungsversuch — etwa 1 Minute nach dem ersten Fehlschlag.
  • 2. Wiederholungsversuch — etwa 4 Minuten nach dem 1. Wiederholungsversuch.
Nach dem zweiten Wiederholungsversuch wird die Zustellung verworfen. Jeder Zustellungsversuch (initialer Versuch + Wiederholungsversuche) wird als separater Eintrag in der Business Console unter dem Tab Zustellungen des Ziels protokolliert, sodass Sie jeden Versuch erneut abspielen oder überprüfen können. Jeder Versuch im Zustellungs-Log eines Ziels enthält eine attempt-Nummer: 1 für die erste Zustellung eines Ereignisses und jeweils eins mehr als der Versuch, den er wiederholt, für jeden Wiederholungsversuch oder erneuten Versand. Automatische Wiederholungsversuche gehen an die URL, an die der erste Versuch gesendet wurde, selbst wenn Sie die url des Ziels zwischenzeitlich geändert haben. Ein manueller erneuter Versand geht an die aktuelle URL des Ziels, sodass die Korrektur einer URL auch den erneuten Versand korrigiert. Weitere wichtige Zustellungsregeln:
  • Bei der ausgehenden HTTP-Anfrage tritt nach 5 Sekunden eine Zeitüberschreitung auf.
  • Didit blockiert Zustellungen an private/Localhost-URLs zum Schutz vor SSRF und folgt niemals Weiterleitungen. Verwenden Sie einen öffentlichen HTTPS-Endpoint, der direkt antwortet.
  • Eine Ziel-url, die einen nicht maskierten Backslash, ein Steuerzeichen oder mehr als ein @ in der Authority enthält, wird als fehlerhaft abgelehnt – eine solche URL kann nicht erstellt werden, und eine bestehende kann weder erneut zugestellt noch erneut abgespielt werden. Verwenden Sie eine reguläre https://host/path-URL mit höchstens einem Userinfo-Abschnitt (für Webhook-URLs im Basic-Auth-Stil).
  • 2xx wird als Erfolg gewertet. Timeouts und Verbindungsfehler gelten als wiederholbar (sie erscheinen als 504/503 im Zustellungs-Log); auf 3xx- und 4xx-Antworten außer 404 erfolgen keine Wiederholungsversuche.
  • Bei einem Wiederholungsversuch berechnet Didit Signaturen mit einem neuen X-Timestamp neu, sodass das Paar aus Zeitstempel und Signatur immer zusammenpasst.
  • Wiederholungsversuche und erneute Sendungen werden mit dem zum Sendezeitpunkt aktuellen Signatur-Secret des Ziels signiert.

Zustellungsreihenfolge

Didit garantiert nicht die Reihenfolge, in der Webhooks bei Ihnen eintreffen. Jede Zustellung wird separat eingereiht, und ein Wiederholungsversuch trifft Minuten nach dem ersten Versuch ein, sodass ein status.updated für Approved vor einem früheren für In Progress bei Ihnen eintreffen kann. Richten Sie Ihren Empfänger darauf ein:
  • Behandeln Sie einen Webhook als Signal, dass sich etwas geändert hat. Wenn die Reihenfolge eine Rolle spielt, rufen Sie GET /v3/session/{session_id}/decision/ auf und agieren Sie basierend auf dem aktuellen status.
  • Lassen Sie niemals zu, dass ein älteres Ereignis einen neueren Zustand überschreibt: Vergleichen Sie das created_at des Ereignisses mit dem zuletzt für diesen Datensatz gespeicherten und ignorieren Sie ein älteres.
  • Verwenden Sie event_id zur Deduplizierung von Wiederholungen, da diese über Wiederholungsversuche hinweg unverändert bleibt.
Didit reiht die Ereignisse einer Travel Rule-Übertragung in einer festen Reihenfolge ein, die unter Statusänderung beim Travel Rule-Austausch beschrieben ist, aber ein Wiederholungsversuch kann die Reihenfolge ihres Eintreffens dennoch verändern.

Webhook-Ziel einrichten

Sie können Ziele über die Business Console > API & Webhooks oder über die Management API verwalten.
1

Ziel erstellen

Rufen Sie POST /v3/webhook/destinations/ auf (oder klicken Sie in der Konsole auf Ziel hinzufügen) mit einem label, einer öffentlichen url, der webhook_version ("v3") und einem subscribed_events-Array, das jede Ereignisfamilie enthält, die dieser Endpoint empfangen soll. Mindestens ein Ereignis ist erforderlich; Wildcards existieren nicht.
2

secret_shared_key speichern

Die Erstellungsantwort gibt den secret_shared_key des Ziels zurück. Speichern Sie ihn zusammen mit Ihren anderen Server-Secrets: Er gehört ausschließlich zu diesem Ziel und ist die Eingabe für alle drei HMAC-SHA256-Signaturvarianten. GET /v3/webhook/destinations/{destination_uuid}/ gibt ihn für Aufrufer mit API-Schlüssel sowie für Konsolenbenutzer, die Webhooks lesen dürfen, erneut zurück.
3

Endpoint implementieren

Stellen Sie eine öffentliche HTTPS-POST-Route bereit, lesen Sie den Roh-Body ein, prüfen Sie die Signatur (bevorzugt X-Signature-V2), validieren Sie das Zeitstempelfenster und leiten Sie die Verarbeitung anhand von webhook_type weiter. Geben Sie 2xx zurück, sobald Sie die Aufgabe eingereiht haben – führen Sie rechenintensive Verarbeitungen asynchron durch.
4

Zustellungen prüfen

Beobachten Sie den Tab Zustellungen des Ziels. Fehlerfreie Webhooks antworten innerhalb von ein bis zwei Sekunden mit 200. 5xx/404 löst die Wiederholungsrichtlinie von Didit aus; dauerhafte Fehler werden nach dem zweiten Wiederholungsversuch verworfen.
5

Mit Try Webhook testen

Nutzen Sie die Konsolenszenarien von Try Webhook, um Ihren Endpoint mit Payloads für genehmigte, abgelehnte, in Prüfung befindliche, KYB-, Entitäts-, Aktivitäts- und Transaktionsfälle zu testen, bevor Sie den Produktionsverkehr aktivieren.
Didit-Webhook-Testfunktion in der Business Console

Senden Sie Test-Webhooks direkt aus der Business Console, um Ihren Endpoint vor dem Produktivgang zu validieren.

Sie können so viele Ziele erstellen, wie Sie benötigen. Ein gängiges Muster ist ein Ziel pro Konsument: z. B. KYC-Ereignisse → Ihr Authentifizierungsdienst, KYB-Ereignisse → Ihr Compliance-Operations-Dienst, Transaktionen → Ihre Fraud-Warteschlange. Jedes Ziel hat sein eigenes Secret, seine eigene Version und seine eigene Abonnementliste, sodass ein rotiertes Secret oder ein fehlerhaftes Deployment bei einem Konsumenten die anderen nicht beeinträchtigt.

Codebeispiele

Um die Sicherheit Ihres Webhook-Endpoints zu gewährleisten, überprüfen Sie die Authentizität eingehender Anfragen mithilfe des secret_shared_key des Ziels.
Verwenden Sie X-Signature-V2. Dies ist die Variante, die gegenüber einer erneuten Codierung durch Middleware am robustesten ist, und sie authentifiziert den Body vollständig (einschließlich decision).

Node.js / Express (empfohlen: X-Signature-V2)

Wenn Sie die Roh-Bytes auslesen können, bevor ein Parser sie verarbeitet, funktioniert die ursprüngliche Variante X-Signature auf dieselbe Weise, signiert jedoch den Roh-Body. Der Vollständigkeit halber führen wir sie unten im Legacy-Abschnitt auf.

Python / FastAPI (empfohlen: X-Signature-V2)

PHP / Laravel (empfohlen: X-Signature-V2)

Die JSON-Standardeinstellungen von PHP reproduzieren die kanonische Form nicht. Vier davon müssen überschrieben werden; fehlt auch nur eine einzige, ändern sich die signierten Bytes, sodass der Digest niemals übereinstimmt: Die Falle mit leeren Objekten ist die tückischste. Didit-Payloads enthalten regelmäßig leere Maps wie risk_view.countries.risk_scores, risk_view.crimes.risk_scores und risk_view.custom_list, sodass ein assoziativer Roundtrip die Signatur bereits bei gewöhnlichen genehmigten Sitzungen bricht und nicht erst bei seltenen Grenzfällen.
Zwei weitere wichtige Punkte:
  • Ganzzahlige Floats erfordern in PHP keine besondere Behandlung. Didit serialisiert sie als Integer (100.0 → 100), und PHPs json_encode schreibt einen ganzzahligen Float auf dieselbe Weise, sodass im Gegensatz zum obigen Node.js-Beispiel kein Schritt wie shortenFloats erforderlich ist.
  • Belassen Sie serialize_precision bei -1 (der Standardwert seit PHP 7.1). Wenn Ihr Host dies auf 17 überschreibt, schreibt json_encode 87.42 als 87.420000000000002 und jede Prüfung von X-Signature-V2 schlägt fehl.
Wenn Sie die kanonische Form nicht zur Übereinstimmung bringen können, ist das obige verifySignatureRaw der einfachere Fallback: Laravels $request->getContent() gibt die unveränderten Bytes zurück, sodass X-Signature funktioniert, solange nichts vor PHP den Body neu codiert. Genau für diesen Fall wurde X-Signature-V2 entwickelt, weshalb V2 zuerst versucht wird.

Testen

Sie müssen keine vollständige Verifizierung durchführen, um Ihren Endpoint zu testen.
  • Wählen Sie unter Business Console > API & Webhooks > Try Webhook ein Szenario aus (z. B. approved_full_features, declined_face_mismatch, in_review_aml_hit, business_session_approved, transaction_created, activity_created, user_status_updated) und senden Sie einen vollständig formatierten Webhook an Ihre Ziel-URL.
  • Jedes Szenario wird mit denselben drei Signatur-Headern und derselben kanonischen Codierung signiert wie der Produktionsdatenverkehr, einschließlich Unicode-Testdaten (approved_kyc_with_unicode), damit Sie bestätigen können, dass X-Signature-V2 auch über Ihre Middleware hinweg funktioniert. Testzustellungen enthalten Beispieldaten und sind mit einem zusätzlichen Header X-Didit-Test-Webhook: true (sowie dem Suffix (Test) im User-Agent) gekennzeichnet, damit Sie diese aus Ihrem Produktionssystem heraushalten können.
  • Führen Sie beliebige historische Zustellungen über den Tab Zustellungen eines Ziels erneut aus, wenn Sie eine Regression debuggen müssen.
Für End-to-End-Smoketests führen Sie eine echte Verifizierung über einen Test-Workflow in Ihrer eigenen Anwendung durch – die resultierenden Webhooks verwenden dieselbe Payload-Struktur, dieselben Header und dieselbe Wiederholungsrichtlinie wie die Szenarien unter Try Webhook.

Beispiele

Genehmigte KYC-Sitzung (status.updated)

Abgelehnte Sitzung mit Warnungen (status.updated)

Genehmigte Unternehmenssitzung (status.updated, KYB)

Transaktion erstellt (transaction.created)

Travel Rule-Austauschstatus geändert (travel_rule.status.updated)

travel_rule_status enthält einen der 13 Austauschstatus, die unter Travel Rule dokumentiert sind. rail ist INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA oder EMAIL (leere Zeichenkette, bis ein Rail ausgewählt wird, falls keines aufgelöst werden konnte, und stets bei Sunrise-Registrierungen) oder TEST_VASP für einen Sandbox-Transfer, den der Didit Test VASP beantwortet hat, und direction ist INBOUND oder OUTBOUND. Ein Sandbox-Ereignis enthält "environment": "sandbox", und ein TEST_VASP-Ereignis enthält außerdem test_vasp: true sowie test_vasp_scenario (das Szenario, das die Antwort geliefert hat). Wenn Didit eine Transaktion für einen Travel Rule-Transfer erstellt (ein POST /v3/transactions/-Aufruf, eine Sunrise-Registrierung nach Einzahlung, eine eingehende TRP-Anfrage oder der eingehende Abschnitt des INTERNAL-Rails), sendet es zuerst das transaction.created-Ereignis für diese Transaktion und danach die travel_rule.status.updated-Ereignisse des Transfers. Eine Regel, die den Status der Transaktion während dieses Schritts ändert, kann auch transaction.status.updated vor transaction.created senden, und eine Sunrise-Registrierung, die sich an eine bereits erstellte Einzahlungstransaktion anfügt, sendet kein transaction.created. Jeder Webhook wird separat zugestellt und wiederholt, sodass ein wiederholtes Ereignis dennoch nach einem späteren eintreffen kann: verwenden Sie transaction_id als Schlüssel in Ihrem Handler und rufen Sie die Transaktion erneut ab, wenn Sie deren aktuellen Zustand benötigen. Ein über TRUST gesendeter Transfer erhält niemals ein COMPLETED-Ereignis, da TRUST keine von Didit lesbaren Daten zurücksendet: er verbleibt bis zur Bestätigungsfrist auf AWAITING_COUNTERPARTY und endet dann mit EXPIRED. Über GTR, VerifyVASP und Sygna Bridge liest Didit die Antwort der Gegenpartei aus dem Netzwerk, und das Ereignis folgt darauf (siehe Travel Rule-Netzwerke).

Workflow-Compliance geändert (workflow.compliance_changed)

transition.from und transition.to entsprechen einem der Werte compliant, gaps_detected, pending_change, unknown_coverage oder not_evaluated. severity ist actionable, wenn Lücken erkannt wurden, indicative bei einer ausstehenden Änderung und informative, wenn Lücken behoben wurden. Didit sendet ein Ereignis pro geändertem Workflow.

Transaktionsregel-Vorschläge erstellt (transaction_rule_suggestions.created)

kind ist ADD für eine neue Regel, TWEAK für eine Änderung an einer bestehenden Regel und DISABLE, um eine Regel zu deaktivieren. rule_id ist für ADD gleich null. open_count ist die Anzahl der nach dem Durchlauf noch offenen Vorschläge, einschließlich der neuen. Über die Einstellungen für Regelvorschläge wird dieser Webhook aktiviert oder deaktiviert; siehe Transaktions-Webhooks.

Investigator-Ereignisse (investigator.*)

Die zwölf investigator.*-Ereignisse verwenden denselben Envelope (event_id, webhook_type, timestamp, created_at, application_id, environment) und dieselben Signatur-Header wie jedes andere Ereignis. Sie enthalten ausschließlich IDs und Zahlen: niemals einen Namen, eine von einem Prüfer eingegebene Begründung, eine Schilderung oder Eingaben des Endbenutzers. Sie enthalten keine session_id; das betroffene Element wird in item benannt.
provenance gibt an, welche Version des Investigators den Bericht erstellt hat:
  • engine ist die Version der Investigator-Engine: eine opake achtstellige ID. Sie ändert sich bei jeder Änderung der Engine, sodass zwei Berichte mit derselben engine von derselben Engine erstellt wurden.
  • agent_version ist die Version des Agenten.
  • policy_version ist die Version Ihrer eigenen Richtlinie, die der Bericht befolgt hat.
  • model_id ist ein veralteter Alias, der denselben Wert wie engine trägt. Lesen Sie stattdessen engine aus.
  • prompt_hash wird nicht mehr gesendet.
item.kind benennt die Art des Elements (eine Sitzung, eine Unternehmenssitzung, eine Transaktion oder ein Fall) und item.id dessen ID. Zeitstempel, die auf _at enden, sind ISO 8601-Zeichenketten. Abonnieren Sie diese Ereignisse, indem Sie die genauen Zeichenketten wie bei jedem anderen Ereignis in subscribed_events auflisten.

Status der Benutzerentität geändert (user.status.updated)

Erneut eingereichte Sitzung

Legacy: X-Signature mit Raw-Body

X-Signature wird weiterhin bei jeder Zustellung gesendet; es handelt sich um HMAC-SHA256 über die exakten Bytes, die Didit überträgt (json.dumps(..., sort_keys=True, separators=(",", ":"))). Verwenden Sie diesen Header nur, wenn Sie den Raw-Body auslesen können, bevor eine Middleware ihn verändert.

Legacy: V2-Webhook-Format

Die nachfolgenden Felder gelten nur, wenn ein Webhook-Ziel auf webhook_version: "v2" festgelegt ist. Für Ziele, die vor V2 erstellt wurden, existiert zudem eine noch ältere "v1"-Festlegung (eine dritte, singuläre Entscheidungsstruktur) – kontaktieren Sie den Support, bevor Sie sich darauf verlassen. Neue Integrationen sollten V3 (Standard) verwenden, das oben dokumentiert ist und plurale Arrays nutzt.
Die funktionsspezifischen Objekte selbst sind in beiden Versionen identisch: Ein V2-Ziel erhält exakt dieselben Treffer-Werte für match_score, risk_score, score_breakdown, risk_view und review_status sowie den aggregierten score wie V3 (siehe AML-Bericht). Die Festlegung auf V2 ändert lediglich, an welcher Stelle im decision-Objekt der AML-Bericht liegt, nicht dessen Inhalt. Falls ein nachgelagertes System weiterhin V2-Strukturen benötigt, erstellen Sie ein separates Webhook-Ziel mit webhook_version: "v2" und belassen Sie Ihre neuen Systeme auf V3.

Verwandte Themen

  • API für Webhook-Ziele – Webhook-Ziele auflisten, erstellen, aktualisieren, löschen und Zustellungs-Logs prüfen.
  • Webhook-Ziel erstellen – Anfrage- und Antwortschema einschließlich des secret_shared_key des Ziels.
  • Datenmodelle – Kanonisches Schema für jedes in decision eingebettete funktionsspezifische Objekt.
  • Entscheidungsschema – Vollständige Feldliste für das in Sitzungs-Webhooks eingebettete decision-Objekt.
  • Verifizierungsstatus – Genaue status-Werte und oben erwähnte Übergänge.
  • Rate-Limiting – Limits, die gelten, wenn GET /v3/session/{id}/decision/ als Fallback per Polling abgefragt wird.
Zuletzt geändert am 7. November 2026