POST request to your configured webhook endpoint with the event body.
Configuring your endpoint
Your endpoint must be publicly reachable over HTTPS and respond with any 2xx status code. Configure its URL with your Meru contact or via the dashboard. On creation you receive a signing secret (prefixed withwhsec_) — store it securely; you need it to verify signatures.
Event structure
Every delivery body has the same top-level shape:string
The event type (see the events below).
string
ISO 8601 timestamp of when the event was emitted.
object
The event payload. Its shape depends on
type.Events
Virtual accounts have no lifecycle events
There is novirtual_account.created, virtual_account.activated or virtual_account.deactivated event. Creating a virtual account, and its activation or deactivation, do not produce a webhook.
The only signal tied to a virtual account is the deposit itself: when funds arrive, you receive a payin.updated. Use the account endpoints in the API if you need its current state.
Migrating event names
We’re standardizing some event names. During the transition, both the new and the legacy name are delivered for the same underlying event, so nothing is missed while you migrate. Each event carries a stabledata.eventId — identical across the new and legacy names — so you can deduplicate the pair.
The legacy names will be removed on 15 November 2026. Point your handlers at the new names before then.
Versioning and deprecation
Webhook events are versioned by event type, not by a header or a field in the payload. Your endpoint subscribes to specific event types, so keeping the version in the name is what lets you choose which version you receive.What counts as a breaking change
Adding a new optional field todata is not breaking, and we ship it without a new version — it’s announced in the changelog. Your handler must ignore fields it doesn’t recognize.
Breaking changes are removing or renaming a field, changing its type, or changing the meaning of a value. We never apply those to an existing event type. Instead we publish a new one with a version suffix — for example payin.updated.v2 — and deliver both in parallel during the deprecation window.
Deprecation policy
When we deprecate an event type or a field:- We announce it at least 90 days before removal, through the changelog, a notice on the event’s reference page carrying the removal date, and an email to the subscribers of the affected event.
- The deprecated event and its replacement are delivered in parallel for the whole window, each carrying the same
data.eventIdso you can deduplicate the pair. - We don’t remove a deprecated event until its subscribers have migrated.
Delivery headers
Each request includes these headers:Verifying signatures
Each request is signed with HMAC-SHA256 so you can confirm it came from us. The signed content is the string{webhook-id}.{webhook-timestamp}.{rawBody}, keyed with your signing secret (base64-decoded after stripping the whsec_ prefix), and the result is base64-encoded into the webhook-signature header.
To verify: recompute the signature over the raw request body (before any JSON parsing) and compare it against the header in constant time. Reject deliveries whose webhook-timestamp is more than 5 minutes old.
Idempotency
The same event may be delivered more than once. Use thewebhook-id header as the idempotency key: store processed IDs and skip duplicates.
Retries
If your endpoint does not return a2xx status (or times out), we retry delivery automatically with exponential backoff over several hours, honoring Retry-After on error responses. Acknowledge quickly (under a few seconds) and process heavy work asynchronously. Endpoints that fail persistently may be disabled.
Best practices
- Always verify the signature against the raw body before processing.
- Respond fast with a
2xx, then process asynchronously. - Be idempotent using
webhook-id. - Don’t assume order — rely on
state/previousStateandupdatedAt, not delivery order. - Log the
webhook-id,type, anddataof every delivery.
Example handler
Testing
Expose your local server with a tunnel (e.g. ngrok) and register the public URL as your webhook endpoint:Troubleshooting
- Invalid signature: verify against the raw body (not re-serialized JSON), use the correct
whsec_secret, and read thewebhook-id/webhook-timestamp/webhook-signatureheaders as-is. - Duplicate events: deduplicate using
webhook-id. - Missed events: ensure your endpoint returns
2xxquickly; non-2xx responses and timeouts are retried, but persistent failures can disable delivery. - SSL errors: your endpoint needs a valid TLS certificate.
webhook-id.