Skip to main content
Anyone can send a POST to your webhook URL. Check the signature on every delivery, and reject it if it doesn’t verify.

The signature header

The header can contain more than one v1 entry. For 24 hours after you rotate the secret, deliveries are signed with both the new and the previous secret. Accept the delivery if any v1 matches.

How to verify

  1. Read the raw request body as bytes, before any JSON parsing. Re-serialising parsed JSON changes whitespace and key order, and the signature will no longer match.
  2. Split the header on ,. Split each part on the first =. Take the t value and every v1 value.
  3. Compute HMAC-SHA256 with your secret (the whole whsec_… string, used as-is) over t, a literal ., and the raw body.
  4. Compare your result with each v1 using a constant-time comparison.
  5. Reject the delivery if |now − t| is more than 300 seconds. This stops an intercepted request being replayed later. Keep your server clock in sync with NTP.
Return a 4xx for a delivery that fails verification. ChargeExpress will retry it, which helps if the failure was a misconfiguration on your side that you then fix.

Samples

Each sample exposes one function that takes the raw body, the header value, and one secret or a list of secrets (useful while you switch to a rotated secret).

Getting the raw body

Most frameworks parse JSON for you, which loses the exact bytes. Read the raw body on the webhook route only.

Troubleshooting