Outbound webhooks are part of the versioned API expansion roadmap. The signature contract documented here is what integrators should align against today so the wire format does not change at GA.
The contract
WiseYield sends three headers with every webhook delivery:
The body is JSON:
Header fallback (Svix parity)
WiseYield readswebhook-* first and falls back to svix-* on the way in, and emits both header sets on the way out for downstream compatibility. Your handler should do the same:
Verifying the signature
The canonical approach is to use the officialstandardwebhooks (or svix) library — it handles timing-safe comparison, multi-signature parsing, and replay-window validation correctly.
Why raw body matters
The signature is computed over the raw request body bytes, not the JSON-parsed object. Most web frameworks parse JSON by default — you must read the raw body before parsing, or signature verification will fail intermittently when whitespace or key ordering differs.- Express: use
express.raw({ type: 'application/json' })on the webhook route. - Next.js App Router: use
request.text()beforeJSON.parse(). - Flask: use
request.data(notrequest.json). - FastAPI: use
await request.body().
Idempotency
Webhooks can be delivered more than once — retries on network blips, processing failures on your end, etc. Usewebhook-id as the dedupe key:
webhook_id plus ON CONFLICT DO NOTHING is the simplest form. WiseYield uses exactly this pattern internally for the webhooks it consumes.
Replay window
webhook-timestamp is signed alongside the body. Reject deliveries older than ~5 minutes to prevent replay attacks:
standardwebhooks and svix libraries enforce this automatically.
Retry behavior
When your endpoint returns a non-2xx response (or fails to respond within 30 seconds), WiseYield retries with exponential backoff:
After 9 failed attempts, the delivery is marked permanently failed and surfaced in your webhook dashboard. Return
200 as soon as you’ve verified the signature and queued the event for processing; do any heavy work asynchronously.
What to log on verification failure
When verification fails, log loudly enough to diagnose:- Body was JSON-parsed before signature check (always read raw bytes first).
- Wrong endpoint secret (rotate, store in env, never commit).
- Reverse-proxy stripping or re-casing headers (check for
Webhook-Idvswebhook-id). - Reading the wrong header set (you sent
svix-*, your code only looks forwebhook-*, or vice versa).
See also
- Standard Webhooks specification — the canonical reference
- Errors & status codes — return shapes from your endpoint