Spectra
Browse docs

webhooks

Signing requests

How to sign your webhook POSTs with HMAC-SHA256 so Spectra verifies them.


Every POST to a Spectra webhook must carry an HMAC-SHA256 signature of the body. Unsigned or invalid-signature requests are rejected with 401 Unauthorized.

The signature

X-Spectra-Signature: sha256=<hex>
X-Spectra-Timestamp: <unix-ms>

The signature is computed over <timestamp>.<body> (period-separated):

import hmac, hashlib, time
secret = b"<your secret bytes>"
ts = str(int(time.time() * 1000))
body = '{"action":"buy","symbol":"BTCUSDT","qty":1}'
mac = hmac.new(secret, f"{ts}.{body}".encode(), hashlib.sha256).hexdigest()
headers = {
  "X-Spectra-Signature": f"sha256={mac}",
  "X-Spectra-Timestamp": ts,
  "Content-Type": "application/json",
}

Why the timestamp

Including the timestamp in the signed payload binds the message to a moment. A captured-and-replayed message past the 5-minute window fails verification — see Replay protection.

Server-side verification

Spectra's worker:

  1. Reads X-Spectra-Timestamp. Rejects if missing or older than 5 min.
  2. Reads X-Spectra-Signature. Rejects if missing or malformed.
  3. Recomputes HMAC over <timestamp>.<body> with the secret.
  4. Compares with constant-time equality. Rejects on mismatch.
  5. Validates the body schema (see Payload schema).
  6. Submits to the broker.

Common mistakes

  • Signing the body without the timestamp prefix.
  • Using == instead of constant-time comparison on your own server (timing attack risk; not Spectra's problem but worth getting right if you proxy our webhook).
  • Sending JSON with whitespace differences from what was signed — always sign the exact bytes you POST.