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:The event type (see the events below).
ISO 8601 timestamp of when the event was emitted.
The event payload. Its shape depends on
type.Events
| Event | Description |
|---|---|
payout.update | A payout changed state |
balance.updated | A deposit (fiat or crypto) changed state |
customer.status.updated | Customer KYC/KYB/status changed |
customer.product.request.updated | Product onboarding/provisioning progressed |
card.transaction.* | A card transaction was created/updated |
Delivery headers
Each request includes these headers:| Header | Description |
|---|---|
Content-Type | application/json |
webhook-id | Unique message/delivery ID. Use it for idempotency. |
webhook-timestamp | Unix timestamp (seconds) of the delivery, used for signature verification. |
webhook-signature | Space-separated list of v1,<base64> signatures (more than one during secret rotation). |
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.