Skip to main content
Transaction Monitoring emits three webhook events that together cover the full transaction lifecycle. All ride on the standard Didit webhook infrastructure with HMAC-SHA256 signing - see the webhooks reference for destinations, signing, and testing.

Event catalog

Entity-level events (user.status.updated, business.status.updated) can also fire as a consequence of transaction activity - see entity webhooks.
Sandbox applications do not emit transaction or travel-rule webhooks - test deliveries against a live application, or use API & Webhooks → Try Webhook in the console.

Envelope

Didit webhooks use a flat body: the event fields sit at the top level next to the envelope fields - there is no nested data object. Every delivery carries:

Payload shapes

transaction.created and transaction.status.updated

Both events share one payload - a compact summary plus the action_required block (or null):
The payload is deliberately thin: on receipt, fetch the full record with GET /v3/transactions/{transaction_id}/ - it returns the complete detail (parties, rule_runs, provider_results, travel_rule, cost_breakdown, decision_reason_code), including why the status changed.

travel_rule.status.updated

Fires on every transition, including the move into FINISHED, CANCELLED, or EXPIRED; once a transfer reaches one of those, no further travel-rule events are sent for it. An event can repeat the status of the previous one, for example when a resend again finds no rail and the transfer stays COUNTERPARTY_VASP_NOT_FOUND. rail is INTERNAL, TRP, GTR, TRUST, VERIFYVASP, SYGNA, EMAIL, or "" while no rail is selected (always "" on sunrise registrations). On transaction creation, a sunrise registration, an inbound TRP inquiry, or the INTERNAL-rail inbound leg, Didit queues the initial travel_rule.status.updated events before transaction.created for the same transaction. A rule that changes the transaction’s status during that step can also send transaction.status.updated before transaction.created. A sunrise registration that attaches to a deposit transaction you already created sends no transaction.created. Webhooks can still arrive in any order, so do not rely on arrival order: key your handler on transaction_id and re-read the transaction when you need its current state.

Example status journeys

Simple approval

Review-then-approve

Step-up verification

Travel Rule transfer

The first two events come from the same request, and the Travel Rule event is queued first; do not rely on that order when they arrive. A transfer sent over GTR, TRUST, or VerifyVASP never gets a COMPLETED event today, because Didit does not process replies on those networks: it stays at AWAITING_COUNTERPARTY until the confirmation deadline and then ends EXPIRED.

Signature verification

Every delivery is HMAC-SHA256 signed with the destination’s shared secret and carries three signature headers plus a timestamp:
See the webhooks reference for the full verification code, including the timestamp freshness check.

Retries and delivery guarantees

  • On a 5xx, 404, timeout, or connection failure, Didit retries up to 2 times: roughly 1 minute after the initial failure, then roughly 4 more minutes after that.
  • Retried deliveries reuse the same event_id - de-dupe on it in your handler.
  • After the final failure, delivery for that event stops and your organization is notified; destinations are never silently disabled.
  • Return a 2xx fast (enqueue, then process) - slow ACKs risk timeouts and duplicate deliveries.

Idempotency

Process each webhook exactly once by storing event_id in your own dedupe store. Re-delivery of the same event is possible when your endpoint ACKs slowly or disconnects mid-request.

Subscribing

Next steps

Webhooks reference

Destinations, signing, retries.

Statuses

Full state machine.

Integration guide

Handling webhooks in production.