The headers we send
Which endpoints are signed. Every endpoint created on or after 24 September 2026 is signed. Endpoints created before that date are not signed unless we have switched signing on for you. Until then they receive only the plain-text
X-Webhook-Secret and X-Auth-Secret headers, with no end date. To have an older endpoint signed, contact support, or delete it and create it again.
Verifying a signature
- Read the raw request body as bytes, before any JSON parsing. Do not parse the JSON and serialise it again: any difference in spacing, key order or escaping changes the signature, and verification fails even though the call is genuine.
- Compute
"sha256=" + hex(HMAC-SHA256(key = your secret as UTF-8, message = raw body)), using lowercase hex. - Compare it with
X-Platnova-Signaturein constant time, using one of the functions below, never==. - If they match, accept the call. During a rotation, also accept a match on
X-Platnova-Signature-Previous. - If neither matches, answer with a non-2xx status, such as
401. See Answer honestly.
secret and the body {"event":"webhook.test"} (24 bytes, no trailing newline), the signature is:
Answer honestly
We judge a delivery only by your HTTP status code. Any 2xx counts as delivered, and that event is never sent again. We cannot see what your code decides after it answers. So if your verification fails, answer with a non-2xx status (for example401). If you answer 200 with something like {"received": true, "verified": false}, we record the event as delivered and stop retrying. Your system has then dropped the event, and neither side gets any warning. A non-2xx answer gets the event retried, shows up as a failure in your dashboard, and, if it keeps happening, pauses the endpoint and emails you.
Key id
X-Platnova-Key-Id names the secret that produced the signature. It is the first 8 hex characters of SHA-256 of the secret, so it identifies a secret without revealing it. It is not secret itself, and it is safe to log.
Use it when you hold more than one Platnova secret. For example, two Platnova businesses might post to one URL, or you might be part-way through a rotation. Look up the secret by key id, then verify with that secret. Do not try each secret in turn.
To work out the key id of a secret you hold:
GET /v1/webhooks show each endpoint’s key_id (and previous_key_id during a rotation), so you can match what you hold against what we send.
Rotating the secret
Rotate an endpoint’s secret from the dashboard: Settings → Webhooks → the endpoint’s menu → Rotate secret. The dashboard callsPOST /v1/webhooks/{id}/rotate-secret, which needs a signed-in session with permission to update webhooks; it cannot be called with an API key.
The response carries the new secret. It is shown once. We never return a secret again, so store it before you close the dialog.
X-Platnova-SignatureandX-Platnova-Key-Id, computed with the new secret.X-Platnova-Signature-PreviousandX-Platnova-Key-Id-Previous, computed with the old secret.
- Rotate, and store the new secret beside the old one.
- Accept a call if either signature verifies against a secret you hold.
- Switch your receiver to the new secret.
- Once
previous_secret_expires_athas passed, the-Previousheaders stop arriving. Delete the old secret.
Unsigned endpoints have no overlap. An endpoint that is not signed only receives the secret in plain text, in
X-Webhook-Secret and X-Auth-Secret. After a rotation those headers carry the new secret straight away. Update your receiver first, or ask us to sign the endpoint.Sending a test event
To check your receiver end to end, use Send test event in the endpoint’s menu. The dashboard callsPOST /v1/webhooks/{id}/test, which sends this body straight away, signed exactly like a real delivery:
A test event is not stored as an event. It does not appear in your event history, and it never counts towards pausing the endpoint.

