Skip to main content
OMS delivers webhooks for every meaningful state change. Subscribe with POST /webhooks, passing a url and the subscriptions you want. For building the endpoint that consumes these events, see Receiving webhooks. The create response returns a one-time signingKey; store it immediately, since it is never returned again. Verify every delivery with the Webhook-Signature header before processing, and deduplicate on the envelope’s id so a redelivered event is handled once.

Choosing your subscriptions

The subscriptions array accepts concrete event names from the catalog below, or the * wildcard.
* does not cover the compliance review events. Those are excluded from the wildcard by design and must be named explicitly.
POST /webhooks/{webhookId}/test sends a webhook.test event to your endpoint. It is delivered regardless of your subscriptions, so you can verify signing and connectivity before any real event fires.

Event envelope

A delivery carries the resource that changed under data, so you rarely need a follow-up read. Most events use the shape below.
The resource’s status lives inside data, not on the envelope: branch on data.status (or the event name) rather than expecting an envelope-level status field.

The *.statusChanged events

The five *.statusChanged events (transaction, virtualAccount, depositAddress, externalAccount, and counterparty) use a variant of this shape. It names the event type in eventType rather than event, and adds two fields:
Write your handler to read event ?? eventType. That one line covers both shapes and every event in the catalog.

Transactions

Transactions emit a single event on every status transition. The full transaction arrives under data, so branch on data.status, using data.subStatus for the detail that distinguishes cases such as a payout held for review. The envelope’s previousStatus tells you which status the transaction left. See the transaction lifecycle for the statuses this event reports and the order they occur in.

Cash-in

Virtual accounts

Lifecycle events report the account itself. The deposit, cryptoTransfer, and fiatTransfer events report the individual legs of money moving through it.

Deposit addresses

Lifecycle events use the depositAddress. prefix. Per-deposit and per-payout legs use the deposit_address. prefix. The fiat payout legs report a returned state because a bank can reverse a settled transfer. The crypto payout leg has no returned event.

External accounts

Counterparties

Endorsements

Wallets

Compliance review events

These events fire when a transaction is held for compliance review. They are excluded from the * wildcard and must be named explicitly in subscriptions.

Delivery and verification

  • Delivery is at-least-once, and ordering is best effort rather than a guarantee. Treat deliveries as unordered and possibly duplicated: deduplicate on id, and apply an event only when its sequence is newer than the last one you processed for that resource, so a late delivery cannot overwrite newer data.
  • Signature verification: the Webhook-Signature header is t=<unix>,v1=<hex>, where the hex is an HMAC-SHA256 over <t>.<raw request body> keyed with your endpoint’s signing key. The timestamp and signature are regenerated on every attempt, so verify against the raw body before parsing it, and reject stale timestamps.
  • Manage subscriptions with the Webhooks endpoints: POST/GET /webhooks, GET/PATCH/DELETE /webhooks/{webhookId}, plus lifecycle actions on POST /webhooks/{webhookId}/enable, POST /webhooks/{webhookId}/disable, POST /webhooks/{webhookId}/rotate-key, and POST /webhooks/{webhookId}/test.
  • Inspect delivery history with GET /webhooks/{webhookId}/deliveries (filter by status, eventId, test, or a createdAfter/createdBefore window). Expand a single delivery with its HTTP attempts with GET /webhooks/{webhookId}/deliveries/{deliveryId}. Requeue failed deliveries with POST /webhooks/{webhookId}/deliveries/retry: pass explicit deliveryIds, or a status plus a createdAfter/createdBefore range to retry all matching deliveries.
  • A webhook is considered down after continuous failure: no successful delivery, and at least one failed attempt since the last success. Two separate thresholds apply. notifyThresholdSecs controls notification only (range 10 minutes to 1 day, default 1 hour), and fires once per down episode rather than per failed attempt. Auto-suspension is separate and happens after roughly 3 days of continuous failure.
  • A suspended webhook keeps creating delivery rows for matching events, but records them as skipped rather than queued, so nothing is silently lost. Only the service sets suspended, and it never clears it: return the webhook to enabled with POST /webhooks/{webhookId}/enable, which resets the down clock, then re-fire the skipped deliveries with POST /webhooks/{webhookId}/deliveries/retry. A disabled webhook differs: it creates no delivery rows at all.
  • Any non-2xx response, timeout, or network error is retried on a fixed schedule. Individual status codes are not special-cased: a 410 Gone is treated the same as a 500.
That is 10 attempts over roughly 3 days, after which the delivery is marked failed. Each attempt is bounded by the webhook’s timeoutMs (default 5000, hard cap 10000), measured from connect through response. A manual retry overrides the cap: it resets the attempt count and requeues the delivery immediately.
  • DELETE /webhooks/{webhookId} is a soft delete: the webhook is hidden from list and get, but delivery history is never removed with it. History is retained for the configured retention window (30 days by default), so existing whd_ links keep resolving.