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:
- Reads
X-Spectra-Timestamp. Rejects if missing or older than 5 min. - Reads
X-Spectra-Signature. Rejects if missing or malformed. - Recomputes HMAC over
<timestamp>.<body>with the secret. - Compares with constant-time equality. Rejects on mismatch.
- Validates the body schema (see Payload schema).
- 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.