All docs pages

Reference

Webhook security

How billing webhooks are verified, deduplicated and stored.

  • Signature first. Every billing webhook is verified against your signing secret over the raw request body, before anything is parsed. An unsigned or mis-signed request gets a 400 and nothing is stored.
  • Replay window. Schemes with a timestamp (Stripe, Paddle, Polar and Dodo's Standard Webhooks) are refused when the timestamp is more than five minutes off.
  • Idempotent. Each event is stored once per program, provider and event id. Re-deliveries and retries are no-ops, so a provider retrying for an hour can't double a commission.
  • Order-independent. A payment that arrives before the signup that names its affiliate is held and attributed the moment the link is made.
  • Secrets at rest (webhook secrets, Stripe restricted keys) are encrypted with AES-256-GCM. API keys are stored as a SHA-256 hash and shown once.
  • One URL per connection. The webhook URL contains your connection's id: /api/webhooks/<provider>/<connection-id>. Disconnecting a provider makes the old URL stop accepting events.
ProviderHeaderScheme
StripeStripe-SignatureHMAC-SHA256 of timestamp.body
Polarwebhook-signatureStandard Webhooks
Lemon SqueezyX-SignatureHMAC-SHA256 hex of the body
PaddlePaddle-SignatureHMAC-SHA256 of ts:body
Creemcreem-signatureHMAC-SHA256 hex of the body
Dodo Paymentswebhook-signatureStandard Webhooks