Webhooks
Every other part of this API is something you ask. Webhooks are the one thing that arrives on its own: when a user receives funds at one of their addresses, we POST a signed event to an HTTPS endpoint you own.
This is the counterpart to the polling loop in Reliability. A deposit has no request of yours behind it, so polling is the only alternative — and that cost grows with every user and every asset you support.
Your webhook endpoint is configured for you by your account manager, along with the signing secret and which event types you want. There is no API call that changes it.
Which deposits produce events
Deposit events are emitted for Ethereum today: native ETH and the ERC-20 assets on it. Deposits on other chains still show up through /balance and /transactions — they just do not push. If you need events on another chain, ask; the catalog grows.
An event is sent only once a deposit has been confirmed, not the moment it appears in a block. A zero-confirmation notification that later disappears is worse for you than a confirmed one a few minutes later.
Event types
wallet.deposit.reverted is informational and rare. A confirmed deposit is never reverted: the confirmation depth is chosen so that a reorganisation deeper than it is out of scope. A dropped transaction can be mined again later — in that case a wallet.deposit.confirmed for the same txHash follows the reverted, carrying a different eventId, and it is the one to credit.
The payload
New fields may be added to data over time. Ignore fields you do not know rather than rejecting the request.
Headers
Verifying the signature
Check the signature before you trust anything in the body. Your endpoint is on the public internet and anyone can POST to it.
The signature is computed over "<t>.<raw request body>", keyed by your signing secret:
Three things that break this in practice:
- Sign the raw bytes, not a re-serialised object. If your framework parses JSON before you see it, key ordering and whitespace change and the HMAC will not match. Capture the body as a string or buffer first.
- Compare in constant time.
==on a hex string leaks how much of the signature you matched. - Enforce the timestamp. Without it, one captured request can be replayed at any point in the future.
Responding
Return any 2xx as soon as you have stored the event. Do the real work afterwards, on your own time.
- Anything that is not a 2xx counts as a failure and is retried.
- Redirects are not followed. A
301or302is a failure — give us the final URL. - Your endpoint must be HTTPS, and must not resolve to a private or loopback address.
- We stop reading your response body after a small number of bytes; there is no way to send data back through it.
Retries
A failed delivery is retried on a fixed schedule — 8 attempts over about 21 hours:
After the eighth failure the event stops being retried. It is not lost — it can be replayed for you on request — but a sustained outage on your side needs a conversation, not a ninth attempt.
Idempotency and ordering
Delivery is at-least-once. The same event can arrive more than once — after a network timeout where your 2xx never reached us, or a retry that crossed with a slow success. Exactly-once delivery is not on offer over a network, and designing as though it were is how double credits happen.
Dedupe on eventId from the verified body, never on the X-Webhook-Id header. The signature covers the body only, so a replayed delivery with a rewritten header would still verify; the eventId, type and data inside the body are the only values you can trust. Record the eventId before you act, and make the second arrival a no-op:
If that inserted nothing, you have already handled this event — return 2xx and stop.
Order is not guaranteed. Two deposits to the same user can arrive in either order, and a retry of an older event can land after a newer one. Use data.blockNumber and createdAt if sequence matters to you; never infer it from arrival order. The same applies to one transaction: a reverted can arrive after the confirmed that superseded it, which is harmless as long as reverted never debits.
A webhook is a notification, not the source of truth. Reconcile against /balance and /transactions — if you have missed events during an outage, that is what closes the gap.