Receiving Webhooks
Every Refersion event is delivered to your endpoint as an HTTP POST with a JSON body. This page
covers what that endpoint has to do to accept a delivery, and how to prove the request came from
Refersion.
Your endpoint
Register a stable HTTPS URL that accepts a JSON POST body. The URL should not change between
deployments, because it is stored against your webhook configuration and every event for that
subscription is sent to it.
Responding to a delivery
Return a status code in the 2xx range (200-299) to acknowledge a delivery. Any other status is treated as a delivery failure and the message is retried, including 3xx redirects — a redirect is not followed, so pointing your endpoint at a URL that redirects means every delivery fails.
Your endpoint has 15 seconds to respond. If it does not answer within that window the delivery is counted as a failure and retried.
Acknowledge first, then process. Return your 2xx response as soon as you have stored or queued the message, and do the real work afterwards, so slow processing never consumes the response window and turns a successful delivery into a retry.
Expect the same message more than once. A retry fires whenever an acknowledgement fails to reach us,
including when your endpoint already handled the delivery, and a portal replay resends messages
deliberately. Every attempt at a message carries the same svix-id, so verify the signature first,
then record that id under a unique constraint in the same transaction that stores or queues the
work. If the id is already recorded, return 2xx without queueing it again, and keep the downstream
processing idempotent so a redelivery cannot apply the same business event twice.
The exact retry timings and the response window described in these guides may change.
Cross-site request forgery protection
Deliveries are server-to-server requests. They carry no cookie and no session, so a CSRF token can
never be present. Disable CSRF protection on the route that receives webhooks — most web
frameworks enable it by default for POST routes, and leaving it on rejects every delivery before
your code runs.
Verifying the signature
Each delivery carries three headers:
svix-id— a unique identifier for the message.svix-timestamp— the Unix timestamp at which the message was sent.svix-signature— one or more HMAC-SHA256 signatures over the message, computed with your endpoint's signing secret.
Prepare the key. The signing secret is issued as a whsec_-prefixed string. Strip the whsec_
prefix and Base64-decode what remains. Those decoded bytes are the HMAC key — not the printable
string you copied out of the portal.
Build the signed content. Join the message id, the timestamp and the raw request body with periods:
{svix-id}.{svix-timestamp}.{raw request body}
Use the body exactly as it arrived. Re-serializing the parsed JSON changes the bytes and the signature will not match.
Compute the expected signature. Take the HMAC-SHA256 of that string with the decoded key, then Base64-encode the raw digest. Base64, not hex — a hex digest never matches.
Compare against the header. svix-signature holds a space-separated list of versioned entries,
each written as {version},{signature}, for example:
v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE= v1,bm9ldHUjKY8ZtTu7fAym7lWZDdU/POqk+2tHOJTMbEQ=
More than one entry appears while a secret is being rotated, and future versions may be added. Split
the header on spaces, discard any entry whose version is not v1, and accept the delivery when your
expected signature matches any remaining entry under a constant-time comparison. Reject it when none
match.
Check the timestamp. Compare svix-timestamp against your own clock and reject anything outside
a 5-minute tolerance. The timestamp is part of the signed content, so it cannot be altered without
invalidating the signature; the tolerance is what makes a captured request unusable once it falls
outside the window.
If your language has a maintained webhook-verification library that implements this scheme, prefer it over a hand-rolled implementation.
Retrieve and rotate an endpoint's signing secret in the webhook settings portal. Merchants open it from Settings > Webhooks; marketplace affiliates open it from "Your Name" > Edit Your Profile. Rotating a secret is the correct response to a secret you believe has leaked.
Integrations built before the current signing scheme may still be verifying an
X-Refersion-Hmac-Sha256 header. Current deliveries do not send it. Verify the three headers above
instead.
Related
- Introduction — how webhooks work and when to use them.
- Retries and Delivery Reliability — what happens when a delivery fails, and how to recover.
- Webhooks for Merchants — the merchant topics and payload examples.
- Webhooks for Marketplace Affiliates — the marketplace topics and payload examples.
- API Reference — how to authenticate, and every endpoint the Refersion API exposes.