Skip to main content
SpherePay signs every webhook delivery so you can confirm it genuinely came from SpherePay and was not tampered with in transit. SpherePay never sends an unsigned delivery. Always verify the signature before processing a payload, and reject anything that fails verification with a non-2xx response.

Delivery headers

Every delivery attempt carries these headers: Headers describe the delivery attempt; the body describes the event. On a replay, Sphere-Timestamp and Sphere-Signature are freshly generated — so the same verification code path handles originals and replays — while the body remains byte-for-byte identical to the original.

How the signature is computed

The signature is an HMAC-SHA256 digest, hex-encoded:
  • secret — the endpoint’s signing secret (whsec_...), returned once when you created the endpoint.
  • timestamp — the value of the Sphere-Timestamp header.
  • rawBody — the raw HTTP request body bytes, exactly as transmitted.
To verify: compute the same digest yourself and compare it to Sphere-Signature using a constant-time comparison.
Verify against the raw request body, not a re-serialized version. Parsing the JSON and re-encoding it can reorder keys or change whitespace, which changes the bytes and breaks verification. Most web frameworks require explicit configuration to expose the raw body — capture it before any JSON middleware runs.

Guard against replay attacks

To prevent captured deliveries from being re-sent by a third party, reject deliveries whose Sphere-Timestamp is more than 5 minutes old. Manual replays get a fresh timestamp and signature, so legitimate replays always pass this check.

Reference implementations

Each snippet takes the raw body bytes, the two headers, and your endpoint secret, and returns whether the delivery is authentic.

Test your verifier

Before the first real event arrives, run your implementation against this known-good test vector. Every input is fixed, so a correct verifier produces exactly the signature shown. Any difference means the bytes you are signing are not the bytes SpherePay signed — almost always a re-serialized body or a missing . separator.
The signed content is the timestamp, a literal ., and the raw body:
The expected Sphere-Signature is:
The vector’s timestamp is a fixed moment in the past, so a verifier that enforces the 5-minute tolerance check rejects it as stale. Check the HMAC step on its own first, then use the signed test delivery below, which signs with the current time, to exercise the full path.

Check without code

Reproduce the signature from a shell with OpenSSL:
Older OpenSSL releases print the prefix as (stdin)= instead of SHA2-256(stdin)=; the hex digest is the same.

Send a signed test delivery to your endpoint

This script plays the part of SpherePay. It signs the test body with the current time and sends it to your receiver with the same headers SpherePay sends. Nothing is sent to or from SpherePay, and nothing is created in your account.
  1. Start your receiver locally. The script posts to http://localhost:3000/sphere-webhook. If your receiver listens somewhere else, change the URL in the last command.
  2. Give the receiver and the script the same secret. Either configure your receiver with the test secret in the script, or replace SECRET in the script with the whsec_ secret of the endpoint you are testing.
  3. Run the script. Paste the whole block into a terminal on macOS, Linux or WSL, where curl and openssl are installed. It creates a fresh timestamp on every run, so the 5-minute tolerance check passes.
  4. Read the response. The -i flag prints the HTTP status line. A correct receiver answers 2xx.
  5. Run it again with a change. Edit one character in BODY or in SECRET and run it again. A correct receiver must answer with a non-2xx status such as 401.

When verification fails

Respond with a non-2xx status code (for example 401) and do not process the payload. The delivery is recorded as failed on SpherePay’s side, giving you an audit trail in the Events API.
If you registered the same receiver URL under multiple SpherePay applications, each application’s endpoint has its own secret. Use the applicationId in the payload to select the correct secret — see Event payloads. If you cannot parse the body before verifying, try each of your known secrets.
Last modified on October 15, 2026