POST to your endpoint the moment it happens, and your app does the corresponding work in your own system: credit a balance, unlock a feature, notify a user, reconcile an order.
The key mental model: a webhook is the trigger for a state change in your product. You don’t poll Etherfuse asking “is it done yet?” — you let the event tell you, then act on the status in the payload.
Webhooks vs. polling. Webhooks fire the instant a state changes. Polling the list endpoints is slower (newly created orders take a moment to index) and wasteful (most requests return “no change”). Use webhooks for anything time-sensitive.
What to do on each event
This is the part most integrations care about: when event X arrives, what should your app do? Each flow below maps the lifecycle to the actions you take in your own system.Onramp — customer buys tokens with MXN
Onramp — customer buys tokens with MXN
Subscribe to
order_updated.| Status | What happened | What your app does |
|---|---|---|
created | Order created; awaiting the customer’s MXN deposit to the depositClabe | Show the customer the depositClabe to pay; mark the order pending |
funded | MXN deposit received and confirmed | Optionally surface “payment received, delivering tokens” |
completed | Tokens delivered to the wallet (confirmedTxSignature has the tx hash) | Mark fulfilled, credit the user, send confirmation |
failed | Order couldn’t be completed | Notify the customer; reconcile |
refunded | Deposit didn’t match the order amount; MXN returned | Reconcile and tell the customer funds were returned |
canceled | Canceled before funding | Close the order out in your system |
Offramp — customer sells tokens for MXN
Offramp — customer sells tokens for MXN
Subscribe to
order_updated.| Status | What happened | What your app does |
|---|---|---|
created | burnTransaction is ready for the customer to sign | Prompt the customer to sign and submit it (or use anchor mode on Stellar) |
funded | The customer’s transaction is confirmed on-chain | Surface “selling, sending MXN” |
completed | MXN sent to the customer’s bank account | Mark the payout as sent |
finalized | Reversal window has passed — funds can no longer be returned | Finalize your accounting for the order |
failed | Couldn’t be completed | Notify the customer; reconcile |
Swap — token to token
Swap — token to token
Subscribe to
Reading the payload (not the status sequence):
swap_updated.| Status | What happened | What your app does |
|---|---|---|
created | sendTransaction is ready for the customer to sign | Prompt the customer to sign and submit it |
funded | The source transaction is confirmed on-chain | Surface “swap in progress” |
completed | Target tokens delivered to the wallet | Update the user’s balances |
failed | Couldn’t be completed | Notify the customer; reconcile |
sendTransaction is always present in the payload. It contains the transaction the customer needs to sign and submit — once signed, the field remains but is no longer actionable. On completion you get two hashes: sendTransactionHash (the customer’s send to Etherfuse) and receiveTransactionHash (Etherfuse’s delivery to the wallet). Which intermediate statuses fire varies by chain (some emit a distinct funds_received update, others don’t), so key your logic off the fields present in the payload rather than expecting a particular status to arrive.Onboarding & compliance — KYC, KYB, bank accounts
Onboarding & compliance — KYC, KYB, bank accounts
Subscribe to
Gating access on these events is the most common compliance pattern: don’t let a customer onramp, offramp, or swap until you’ve received
kyc_updated, customer_updated, and bank_account_updated.| Status | What happened | What your app does |
|---|---|---|
kyc_proposed | KYC data submitted, awaiting review | Show “verification pending” |
kyc_approved / customer_verified | The customer is cleared | Unlock their ability to transact |
kyc_rejected | KYC rejected (payload includes updateReason) | Block transacting; show the reason; prompt resubmission |
bank_account_updated with compliant: false | The bank can’t be used until the customer completes additional verification | Launch the customer into /idv?bank_account_id=<id> for that bank; see Bank account compliance |
bank_account_active | Bank account verified and usable | Enable offramps/payouts to that account |
kyc_approved (or customer_verified) for them.How delivery works
Register an endpoint
Call POST /ramp/webhook with your HTTPS URL and the event type. The response includes a signing
secret, returned only once — store it securely.Receive the event
When the event fires, Etherfuse sends a
POST with the JSON payload to your URL, plus an X-Signature header.Verify the signature
Recompute the HMAC-SHA256 signature with your secret and compare. See Verifying Webhooks for Node and Python code.
Event types
| Event | Fires when | Payload reference |
|---|---|---|
order_updated | An onramp/offramp order changes status | order_updated payload |
swap_updated | A swap changes status | swap_updated payload |
customer_updated | A customer’s verification status changes | customer_updated payload |
kyc_updated | A programmatic KYC submission is reviewed | kyc_updated payload |
kyb_updated | A business organization’s KYB status changes | kyb_updated payload |
bank_account_updated | A bank account’s verification status changes | bank_account_updated payload |
Payloads
Every webhook payload is a JSON object with a single key matching the event type; the value is the entity that changed, signed with your webhook secret (see Verifying Webhooks). The full schema and a worked example for each event live on its reference page, linked from the Event types table above. Fields are omitted whennull or not applicable to the entity’s current state, so drive your logic off the fields present in the payload rather than expecting a fixed shape. A few specifics worth calling out:
order_updatedcarriesburnTransaction,depositClabe, andconfirmedTxSignatureonly when relevant to the order type and status. For embedded (non-custodial) wallets it also carries the pendingapprovalobject (approvalMessageId,approvalMessage,summary) while the order awaits the owner’s signature; there is no separate embedded-wallet transaction event. Re-read the order right before signing so theapprovalMessagetimestamp is fresh.swap_updated—sendTransactionis always present (the transaction to sign; it stays in the payload but is no longer actionable once signed);receiveTransactionHashappears only on completion.bank_account_updated— whilecompliantisfalsethe customer must complete additional verification before the bank can be used; the form depends on the bank type (see Bank account compliance).kyc_updated— on approval,approvedistrueandupdateReason/needsWork/userMessageare omitted.kyb_updatedcarries the business’sorganizationIdandstatus. It fires only when the status actually changes, never on individual answer edits. Theapproved/submitted/notStarted/totalcounts cover the customer-facing requirements only, andapprovedAtis set once the business is approved.
Reliability & best practices
- Retries. Failed deliveries (a non-
2xxresponse or connection error) are retried up to 3 times with 5-second delays. Return a2xxpromptly to avoid them. - Acknowledge fast, process async. Hand the payload to a queue and return
2xxright away; don’t block the response on downstream work. - Handle events idempotently. The same event can arrive more than once (e.g. a retry after your
2xxwas lost). Dedupe on the resource ID plus its status so reprocessing is harmless — this pairs naturally with the client-generated UUIDs you already use for orders. - Don’t rely on ordering. Drive your logic off the
statusin the payload, not the order in which deliveries arrive. - Expose a public HTTPS endpoint. For local development, use a tunnel such as ngrok so Etherfuse can reach your handler.
Verifying signatures
Every delivery is signed so you can confirm it’s authentic:X-Signature: sha256={hex}, computed as HMAC-SHA256 over the RFC 8785-canonicalized JSON body using your webhook secret. Full walkthrough with Node/Python code: Verifying Webhooks.
Managing webhooks
- Create a subscription —
POST /ramp/webhook(returns the one-time secret) - List subscriptions —
POST /ramp/webhooks - Get a subscription —
GET /ramp/webhook/{id} - Delete a subscription —
DELETE /ramp/webhook/{id}
If a transaction expires before the customer signs it, fetch a fresh one with POST /ramp/order/{id}/regenerate_tx or POST /ramp/swap/{id}/regenerate_tx.