The signature header
Each delivery includes anX-Fanvue-Signature header:
tis a Unix timestamp (seconds since epoch) recording when Fanvue signed the request.v0is the HMAC-SHA256 signature of the signed payload, encoded as a hexadecimal string.
Get your signing secret
Which secret signs your deliveries depends on how the webhook was registered. There are two paths, and they do not share a secret:Developer Area apps
The signature is computed with a per-app signing secret. Find it in the Fanvue Developer Area:1
Open the Events tab
Go to the Fanvue Developer Area, select your app, and open the Events tab.
2
Reveal the secret
Click View Signing Secret and copy the value.
3
Store it server-side
Keep the secret in an environment variable (for example
FANVUE_WEBHOOK_SECRET). Never ship it in client-side code or commit it to source control.Webhooks registered through the API
A creator who subscribes an endpoint withPOST /webhooks/subscriptions gets a
per-subscription secret, not the app secret. It is returned as signingSecret
in the create response and looks like whsec_ followed by 64 hex characters:
Deleting one subscription does not affect the secrets of the others. This is why
the secrets are per subscription rather than shared.
How verification works
Recompute the signature yourself and check it matches what Fanvue sent.1
Parse the header
Split
X-Fanvue-Signature on the comma and read t (the timestamp) and v0 (the signature). If either is missing, reject the request.2
Reconstruct the signed payload
Concatenate the timestamp, a period, and the raw request body:
{timestamp}.{body}. Use the exact bytes Fanvue sent. If you parse the JSON first and re-serialize it, the bytes change and the signature will not match.3
Compute the expected signature
Run HMAC-SHA256 over the signed payload using your signing secret, then hex-encode the result.
4
Compare in constant time
Compare your computed signature against
v0 with a timing-safe comparison (crypto.timingSafeEqual in Node, hmac.compare_digest in Python). A plain == can leak the secret through timing differences.5
Reject stale timestamps
Check that
t is within an acceptable window of the current time (5 minutes is a sensible default). This stops an attacker from replaying a previously valid request.Code sample
Both samples take the raw request body and theX-Fanvue-Signature header value, and return whether the request is authentic.
- Node.js
- Python
When verification fails, respond with
401 and do not process the payload. When it succeeds, persist the event and return a 2xx as described in the Webhooks Overview.Test it without waiting for a real event
Use the Test action in the webhook’s … menu (in the Events & Endpoints list) to send a sample payload to your endpoint. It carries a realX-Fanvue-Signature header, so a passing test confirms your verification logic works end to end.