Skip to main content
Proposal, not live spec. A webhook design drawn from Stripe, Svix, Transak, and Plaid. For team review - exact event names, payloads, and the signing scheme are decided with the routing layer.

Structure (Svix’s 7-section shape)

Intro - Events & event types - Adding endpoints - Testing - Signature verification - Retries - Troubleshooting. Every section below maps to one of these.

Event catalog

Each event maps to a transaction status:
EventFires when
deposit.createdA deposit is created
deposit.routingFunds begin settling cross-chain
deposit.source_confirmedSource payment confirmed
deposit.completedUSDC landed - safe to credit the user
deposit.failedDeposit failed
deposit.refundedA completed deposit was reversed
Common envelope: { id, type, created, data: { ... } }.

Signature verification (Stripe HMAC pattern)

  • Header: Rheon-Signature: t=<timestamp>,v1=<signature>
  • Signed payload: {timestamp}.{raw_request_body}
  • HMAC-SHA256 with the endpoint secret, compared in constant time
  • ⚠️ Use the raw body - if the framework re-serializes it, verification fails. This is the single most common integration bug, worth calling out explicitly.
  • Timestamp guards against replay; reject events older than ~5 minutes.
Alternative (Transak): a JWT-signed payload verified with a partner access token. HMAC header is simpler for partners to consume.

Retries, delivery & idempotency

Benchmarks to set expectations:
Provider2xx timeoutRetry windowBackoff
Stripe~5sup to 3 daysexponential
Plaid10sup to 24h30s ×4, honors Retry-After
  • Return 2xx before heavy work: verify → enqueue → return 200, then process async.
  • No ordering guarantee - events can arrive out of order; reconcile against the API.
  • Idempotency - events can arrive more than once; deduplicate on id.

Testing (the quality signal - Stripe)

  • CLI with a localhost tunnel: rheon listen --forward-to localhost:PORT/webhook
  • Trigger the full lifecycle (created → routing → completed) on demand in the sandbox
  • A public tunnel (ngrok) for local endpoints

Troubleshooting

A table of HTTP status / error → cause → fix (connection refused, 3xx redirect, 4xx, 5xx, TLS, timeout, signature mismatch), plus source IP ranges for partners that firewall.