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

# Transaktionsüberwachung Webhooks: Ereignisse & Signatur

> Didit-Webhook-Ereignisse für Transaktionen: Auslöser, Payloads und HMAC-Signaturprüfung. 1,000 Transaktionen/Monat inklusive, danach $0.001 je.

Transaction Monitoring sendet drei Webhook-Ereignisse, die zusammen den gesamten Transaktionslebenszyklus abdecken, dazu ein optionales viertes, das neue Regelvorschläge meldet. Alle werden über die Standard-Webhook-Infrastruktur von Didit mit HMAC-SHA256-Signierung übertragen. Informationen zu Zielen, Signierung und Tests finden Sie in der [Webhook-Referenz](https://docs.didit.me/de/integration/webhooks.md).

## Ereigniskatalog

| Ereignis | Wann es ausgelöst wird |
| - | - |
| `transaction.created` | Eine neue Transaktion wurde angelegt und die initiale Bewertung ist abgeschlossen. Es wird auch für die eingehenden Transaktionen ausgelöst, die Didit für Sie anlegt: eine nach einer Einzahlung erfolgende [Travel-Rule-Sunrise-Registrierung](https://docs.didit.me/de/transaction-monitoring/travel-rule.md#nachträgliche-sunrise-registrierung-nach-der-einzahlung), eine eingehende `TRP`-Anfrage oder eine Überweisung über die `INTERNAL`-Schiene von einem anderen Didit-Kunden. |
| `transaction.status.updated` | Der Status der Transaktion hat sich geändert, etwa durch die Prüfung durch einen Analysten, die Auflösung einer Nachbearbeitungssitzung oder eine Regelaktion, wenn Didit Ihre Regeln nach einem Travel-Rule-Übergang erneut auswertet (zum Beispiel nach einem Nachweis der Wallet-Inhaberschaft). Die Statusänderung, die Ihr `timeout_outcome` anwendet, wenn eine Überweisung abläuft, sendet dieses Ereignis nicht. Reagieren Sie stattdessen auf `travel_rule.status.updated` mit `EXPIRED`. |
| `travel_rule.status.updated` | Ein [Travel-Rule](https://docs.didit.me/de/transaction-monitoring/travel-rule.md)-Austausch hat einen Übergang vollzogen, bei ausgehenden und bei eingehenden Überweisungen (einschließlich des eingehenden Teils über die `INTERNAL`-Schiene, von Sunrise-Registrierungen und von eingehenden `TRP`-Anfragen), einschließlich des Übergangs `NOT_APPLICABLE` unterhalb des Schwellenwerts. Bei einer Transaktion, die Didit anlegt, werden diese Ereignisse nach ihrem `transaction.created` gesendet. |
| `transaction_rule_suggestions.created` | Ein Lauf der Regelvorschläge hat neue Regeländerungen für Ihre Anwendung vorgeschlagen. Das Ereignis wird nur ausgelöst, wenn Sie **Webhook** in den Benachrichtigungseinstellungen für Regelvorschläge aktivieren, und nur für Läufe, die Vorschläge erstellt haben. |

Ereignisse auf Entitätsebene (`user.status.updated`, `business.status.updated`) können ebenfalls als Folge von Transaktionsaktivität ausgelöst werden. Siehe dazu [Entitäts-Webhooks](https://docs.didit.me/de/entities/webhooks.md).

> [!NOTE]
>
> Sandbox-Anwendungen senden keine `transaction.created`- oder `transaction.status.updated`-Webhooks. Um diese Ereignisse zu testen, verwenden Sie eine Live-Anwendung oder **API & Webhooks → Try Webhook** in der Konsole. Travel-Rule-Austauschvorgänge in der Sandbox senden `travel_rule.status.updated`-Webhooks mit `environment: sandbox`. Testen Sie einen Austausch vor dem Go-live mit dem [Didit Test VASP](https://docs.didit.me/de/transaction-monitoring/travel-rule-sandbox.md).

## Umschlag

Didit-Webhooks verwenden einen **flachen** Body: Die Ereignisfelder stehen auf oberster Ebene neben den Envelope-Feldern. Es gibt kein verschachteltes `data`-Objekt.
Jede Zustellung enthält:

| Feld | Beschreibung |
| - | - |
| `webhook_type` | Der Ereignisname, z. B. `transaction.created`. |
| `event_id` | Stabiler Bezeichner des Ereignisses. Dieselbe `event_id` wird bei Wiederholungen wiederverwendet, deshalb deduplizieren Sie anhand dieses Werts. |
| `timestamp` / `created_at` | Unix-Epochenzeit in Sekunden, zu der die Zustellung versendet wurde. |
| `application_id` | Die Anwendung, zu der das Ereignis gehört. |
| `environment` | `live` oder `sandbox`. |

## Payload-Strukturen

### `transaction.created` und `transaction.status.updated`

Beide Ereignisse enthalten denselben Payload: eine kompakte Zusammenfassung plus den Block [`action_required`](https://docs.didit.me/de/transaction-monitoring/sdk-transaction-submission#erforderliche-benutzeraktionen) (oder `null`):

```json theme={null}
{
  "webhook_type": "transaction.created",
  "event_id": "9c0c8b8a-1111-4222-9333-666666666666",
  "timestamp": 1774970000,
  "created_at": 1774970000,
  "application_id": "11111111-2222-3333-4444-555555555555",
  "environment": "live",
  "transaction_id": "9c7d2a30-...",
  "txn_id": "tx-0001",
  "status": "IN_REVIEW",
  "score": 65,
  "severity": "MEDIUM",
  "amount": "500.00",
  "currency": "EUR",
  "direction": "OUTBOUND",
  "action_required": null
}
```

Der Payload ist bewusst schlank. Rufen Sie nach dem Empfang den vollständigen Datensatz über [`GET /v3/transactions/{transaction_id}/`](https://docs.didit.me/de/management-api/transactions/get) ab. Dieser Aufruf liefert alle Details (Parteien, `rule_runs`, `provider_results`, `travel_rule`, `cost_breakdown`, `decision_reason_code`), einschließlich der Begründung, *warum* sich der Status geändert hat.

### `transaction_rule_suggestions.created`

Dieses Ereignis betrifft Ihren Regelsatz und nicht eine einzelne Transaktion. Deshalb enthält es keine Transaktions-ID.
Es enthält niemals personenbezogene Daten, nur Vorschlags-IDs, Arten, Titel und Anzahlen.

```json theme={null}
{
  "webhook_type": "transaction_rule_suggestions.created",
  "run_id": "5b2f0c1e-0000-0000-0000-000000000000",
  "open_count": 3,
  "suggestions": [
    {
      "suggestion_id": "9a7d3e22-0000-0000-0000-000000000000",
      "kind": "...",
      "title": "...",
      "rule_id": "...",
      "expected_impact": {}
    }
  ]
}
```

| Feld | Beschreibung |
| - | - |
| `run_id` | Der Lauf der Regelvorschläge, der die neuen Vorschläge erzeugt hat. |
| `open_count` | Wie viele Vorschläge für die Anwendung nach diesem Lauf offen sind. |
| `suggestions[]` | Die Vorschläge, die dieser Lauf erstellt hat. Jeder enthält `suggestion_id`, `kind`, `title`, `rule_id` (null, wenn der Vorschlag eine neue Regel vorschlägt) und `expected_impact`. |

Die üblichen Envelope-Felder (`event_id`, `timestamp`, `application_id`, `environment`) werden dem Body hinzugefügt.

### `travel_rule.status.updated`

```json theme={null}
{
  "webhook_type": "travel_rule.status.updated",
  "event_id": "9c0c8b8a-1111-4222-9333-777777777777",
  "timestamp": 1774970000,
  "created_at": 1774970000,
  "application_id": "11111111-2222-3333-4444-555555555555",
  "environment": "live",
  "transaction_id": "22222222-3333-4444-5555-666666666666",
  "txn_id": "trv-2026-07-05-001",
  "travel_rule_status": "COMPLETED",
  "rail": "INTERNAL",
  "direction": "OUTBOUND"
}
```

Dieses Ereignis wird bei jedem Übergang ausgelöst, auch beim Übergang in `FINISHED`, `CANCELLED` oder `EXPIRED`. Sobald eine Überweisung einen dieser Zustände erreicht, werden keine weiteren Travel-Rule-Ereignisse mehr dafür gesendet.
Ein Ereignis kann den Status des vorherigen wiederholen, etwa wenn ein erneuter Versuch wieder keine Schiene findet und die Überweisung in `COUNTERPARTY_VASP_NOT_FOUND` bleibt.
`rail` ist `INTERNAL`, `TRP`, `GTR`, `TRUST`, `VERIFYVASP`, `SYGNA`, `EMAIL`, `TEST_VASP` (eine Sandbox-Überweisung, auf die der Didit Test VASP geantwortet hat; sie enthält außerdem `test_vasp: true` und `test_vasp_scenario`) oder `""`, solange keine Schiene ausgewählt ist (bei Sunrise-Registrierungen immer `""`).

Bei der Anlage einer Transaktion, einer Sunrise-Registrierung, einer eingehenden `TRP`-Anfrage oder dem eingehenden Teil über die `INTERNAL`-Schiene sendet Didit zuerst `transaction.created` und danach die `travel_rule.status.updated`-Ereignisse der Transaktion.
Eine Regel, die in diesem Schritt den Status der Transaktion ändert, kann auch `transaction.status.updated` vor `transaction.created` senden.
Eine Sunrise-Registrierung, die an eine bereits von Ihnen angelegte Einzahlungstransaktion angehängt wird, sendet kein `transaction.created`.
Jeder Webhook wird einzeln zugestellt und einzeln wiederholt. Ein erneut zugestelltes Ereignis kann deshalb nach einem späteren Ereignis eintreffen. Verarbeiten Sie Ereignisse anhand von `transaction_id` und lesen Sie die Transaktion erneut aus, wenn Sie ihren aktuellen Stand benötigen.

## Beispiele für Statusverläufe

### Einfache Genehmigung

```
transaction.created (APPROVED, score 0)
```

### Prüfung, dann Genehmigung

```
transaction.created (IN_REVIEW, score 65)
...analyst reviews...
transaction.status.updated (status: APPROVED)
```

### Verifizierung mit erhöhter Sicherheitsstufe

```
transaction.created (AWAITING_USER, action_required.type: verification_session)
...user completes the biometric re-check...
transaction.status.updated (status: APPROVED, action_required: null)
```

### Travel-Rule-Überweisung

```
transaction.created (APPROVED)
travel_rule.status.updated (AWAITING_COUNTERPARTY, rail: EMAIL)
...counterparty accepts the pickup...
travel_rule.status.updated (COMPLETED)
...you broadcast on-chain and PATCH the hash...
travel_rule.status.updated (FINISHED)
```

Die ersten beiden Ereignisse stammen aus derselben Anfrage, und `transaction.created` wird zuerst in die Warteschlange gestellt. Jeder Webhook wird einzeln zugestellt. Verlassen Sie sich deshalb nicht auf die Ankunftsreihenfolge.
Eine Überweisung über TRUST erhält nie ein `COMPLETED`-Ereignis, weil TRUST keine Antwort zurücksendet, die Didit auslesen kann. Sie verbleibt in `AWAITING_COUNTERPARTY` bis zur Bestätigungsfrist und endet dann in `EXPIRED`. Über GTR, VerifyVASP und Sygna Bridge liest Didit die Antwort der Gegenpartei aus dem Netzwerk aus, und das Ereignis folgt dieser Antwort.

## Signaturprüfung

Jede Zustellung wird mit HMAC-SHA256 unter Verwendung des gemeinsamen Geheimnisses des Webhook-Ziels signiert. Sie enthält drei Signatur-Header und einen Zeitstempel:

| Header | Schema |
| - | - |
| `X-Signature-V2` | HMAC über das kanonische JSON (sortierte Schlüssel, Unicode bleibt erhalten) – **empfohlen**. |
| `X-Signature` | HMAC über das kanonische JSON mit ASCII-Escaping (Legacy). |
| `X-Signature-Simple` | HMAC über `"{timestamp}:{session_id}:{status}:{webhook_type}"` – **veraltet**. Transaktions-Payloads enthalten kein `session_id`, daher wird `"{timestamp}::{status}:{webhook_type}"` signiert. Damit werden weder die Transaktion noch ihr Betrag oder Score identifiziert. Ein `travel_rule.status.updated`-Payload enthält ebenfalls keinen `status`, daher wird `"{timestamp}:::travel_rule.status.updated"` signiert. Verifizieren Sie mit `X-Signature-V2`, und lesen Sie [den Hinweis zur Abkündigung](https://docs.didit.me/de/integration/webhooks.md#x-signature-simple-ist-veraltet). |
| `X-Timestamp` | Unix-Epoch-Sekunden – Zustellungen, die älter als 5 Minuten sind, ablehnen. |

```typescript theme={null}
import { createHmac, timingSafeEqual } from 'crypto';

function verify(rawBody: string, signatureV2: string, secret: string) {
  const canonical = JSON.stringify(
    Object.keys(JSON.parse(rawBody)).sort()
      .reduce((acc, k) => ({ ...acc, [k]: JSON.parse(rawBody)[k] }), {})
  );
  const expected = createHmac('sha256', secret).update(canonical).digest('hex');
  return timingSafeEqual(Buffer.from(signatureV2), Buffer.from(expected));
}
```

Den vollständigen Verifizierungscode einschließlich der Prüfung der Aktualität des Zeitstempels finden Sie in der [Webhooks-Referenz](https://docs.didit.me/de/integration/webhooks.md#signaturprüfung).

## Wiederholungsversuche und Zustellungsgarantien

* Bei einem `5xx`, einer `404`, einem Timeout oder einem Verbindungsfehler wiederholt Didit die Zustellung bis zu **2-mal**: etwa 1 Minute nach dem ersten Fehler und danach noch einmal etwa 4 Minuten später.
* Wiederholte Zustellungen verwenden dieselbe `event_id`. Filtern Sie Duplikate in Ihrem Handler anhand dieser ID heraus.
* Nach dem letzten Fehlschlag wird die Zustellung für dieses Ereignis eingestellt, und Ihre Organisation wird benachrichtigt. Webhook-Ziele werden niemals stillschweigend deaktiviert.
* Antworten Sie schnell mit einem `2xx` (zuerst in die Warteschlange einreihen, dann verarbeiten). Langsame Bestätigungen bergen das Risiko von Timeouts und doppelten Zustellungen.

## Idempotenz

Verarbeiten Sie jeden Webhook genau einmal, indem Sie die `event_id` in Ihrem eigenen Deduplizierungsspeicher ablegen. Eine erneute Zustellung desselben Ereignisses ist möglich, wenn Ihr Endpoint langsam bestätigt oder die Verbindung während der Anfrage abbricht.

## Abonnieren

```bash theme={null}
curl -X POST https://verification.didit.me/v3/webhook/destinations/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "TM events",
    "url": "https://yourapp.com/webhooks/didit-tm",
    "subscribed_events": [
      "transaction.created",
      "transaction.status.updated",
      "travel_rule.status.updated"
    ]
  }'
```

## Nächste Schritte

> [card] [Webhooks-Referenz](https://docs.didit.me/de/integration/webhooks.md)
>
> Webhook-Ziele, Signierung, Wiederholungsversuche.

> [card] [Status](https://docs.didit.me/de/transaction-monitoring/statuses.md)
>
> Vollständiger Zustandsautomat.

> [card] [Integrationsleitfaden](https://docs.didit.me/de/transaction-monitoring/integration-guide.md)
>
> Webhooks im Produktivbetrieb verarbeiten.


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