Skip to main content
Every webhook Fanvue sends carries a signature. Verify it on every request so an attacker cannot forge events against your endpoint or replay an old delivery. Reject anything that fails.

The signature header

Each delivery includes an X-Fanvue-Signature header:
  • t is a Unix timestamp (seconds since epoch) recording when Fanvue signed the request.
  • v0 is 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 with POST /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:
The signingSecret is returned only in the response to the call that created the subscription. There is no endpoint that reads it back and no way to reveal it later. Store it before you discard the response. If you lose it, delete the subscription and create a new one — which mints a new secret.
Every subscribe call mints a new destination with its own secret, even for the same URL and the same creator. So if you register several subscriptions, keep a secret per subscription id and select the right one when verifying — do not assume one secret verifies every delivery reaching your endpoint.
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.
Verify against the raw request body, not a parsed-and-re-serialized version. Most frameworks expose the raw bytes through a hook (for example Express’s verify callback). Capture them before any JSON middleware mutates the request.

Code sample

Both samples take the raw request body and the X-Fanvue-Signature header value, and return whether the request is authentic.
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 real X-Fanvue-Signature header, so a passing test confirms your verification logic works end to end.