> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rheon.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Proposal: Webhooks

> Event catalog, signature verification, retries, idempotency, and testing.

<Info>
  **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.
</Info>

## 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](/proposals/transaction-statuses):

| Event                      | Fires when                            |
| -------------------------- | ------------------------------------- |
| `deposit.created`          | A deposit is created                  |
| `deposit.routing`          | Funds begin settling cross-chain      |
| `deposit.source_confirmed` | Source payment confirmed              |
| `deposit.completed`        | USDC landed - safe to credit the user |
| `deposit.failed`           | Deposit failed                        |
| `deposit.refunded`         | A 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:

| Provider | 2xx timeout | Retry window | Backoff                      |
| -------- | ----------- | ------------ | ---------------------------- |
| Stripe   | \~5s        | up to 3 days | exponential                  |
| Plaid    | 10s         | up to 24h    | 30s ×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.
