Skip to main content
Webhooks push real-time events to your backend. This page documents the exact payload Rach sends for every event, the headers, and how to verify signatures. Payments and CaaS each have their own configuration and signature scheme.

Payments webhooks

Configure your endpoint

Set your webhook URL (and enable wallet/deposit events) in the Payments dashboard under Developers → Webhooks. The dashboard shows your signing secret (whsec_…) once — store it to verify signatures.
The webhook_secret (whsec_…) is shown only when you set or rotate it in the dashboard. Store it — you need it to verify signatures. Rotating it invalidates the old one.

Headers on every delivery

Events & exact payloads

There are two payload shapes: checkout/remittance events are flat; wallet (WaaS) events wrap the data in an event + data envelope.
Sent for Crypto Gateway checkout sessions. Flat payload:
network, tx_hash, paid_at and metadata are omitted when not applicable. payment.failed carries the same shape with status: "failed" and no paid_at.

Verify the signature

Compute the HMAC over the raw body and compare in constant time:

Test it

Send a test event and rotate your signing secret from the Payments dashboard (Developers → Webhooks). Rotating invalidates the old secret — update your verifier immediately.

CaaS webhooks

Register your webhook endpoints and choose which events to receive from your CaaS dashboard.

Delivery & verification (every event)

Every webhook is an HTTP POST with a JSON body and these headers:
Signature verification (Node)
Conventions
  • Delivery is best-effort — settlement already happened on-chain; the notification never blocks it. Only webhooks subscribed to that event type receive it. Return 2xx.
  • Amounts are decimal strings with 6 dp (e.g. "5.000000").
  • tx_hash is the real on-chain EVM transaction hash (explorer-lookupable), not the internal userOpHash.
  • Timestamps are RFC3339 UTC.
  • On *.settled / *.failed, empty fields are omitted, not null (reason absent on success; amount_*/token/tx_hash absent on a pre-broadcast failure). Treat missing = unknown.

Transaction events

Fires when a wallet belonging to your business receives funds (incl. cross-merchant). Not sent if the recipient is an unaffiliated phone — they get an SMS instead. Delivered to the recipient merchant.

Account / membership events

Field-name notetransfer.received uses settled_at; the status events (*.settled / *.failed) use updated_at. Both are RFC3339 UTC. If a webhook is missed, poll GET /v1/swaps/:id (or the equivalent transfer endpoint) — it returns the same status fields.
Review delivery logs in your CaaS dashboard.

Best practices

Verify against the raw bodyCompute the HMAC over the exact bytes received. Re-serializing parsed JSON changes the bytes and breaks the signature.
  • Respond fast (2xx) then work async — Rach retries non-2xx deliveries.
  • Be idempotent — dedupe on X-Webhook-ID (Payments, or deposit_id for deposits).
  • Credit only when safe — for deposits, wait for safe_to_credit: true.