Verify Form webhook signatures
For developers - store the Form webhook secret, check each request's signature, ignore repeated deliveries, respond quickly and handle retries safely.
When you send form responses to your own server with a Webhook, Spacebrain signs each request so your server can check it really came from Spacebrain. This guide is for the developer who builds that server endpoint. It covers the Webhook destination in a form's Connect tab only, not Zapier, Make, Slack or Google Sheets, and not webhooks from email, payments, messaging or Webinars.
Before you connect
- Deploy a public HTTPS endpoint that accepts
POSTwithapplication/json. - Ensure it can read the untouched request bytes before JSON parsing or body transformation.
- Choose durable storage for processed delivery identifiers or business-object identifiers.
- Make sure that processing the same delivery twice creates only one record.
- Store secrets in a secret manager, never source control, logs, screenshots, or support tickets.
When you create the Form Webhook connection, Spacebrain displays its signing secret once. Copy it directly into the receiver's secret manager. If it is lost or exposed, disconnect and create a replacement; do not attempt to recover it from logs.
Check the signature on the raw body
For a Form Webhook delivery, use the signature header and algorithm shown by the current connection setup. The receiver must compute its digest from the raw HTTP body bytes, before parsing or reserializing JSON, and compare values with a constant-time function.
The currently documented Spacebrain HMAC format is:
X-SpaceBrain-Signature: sha256=<lowercase hexadecimal HMAC-SHA256>The signed value is the entire raw JSON request body. Even semantically equivalent JSON produces a different digest if whitespace, key order, or escaping changes.
Node.js example
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifySpacebrainSignature(rawBody, signatureHeader, secret) {
if (!Buffer.isBuffer(rawBody)) throw new TypeError('rawBody must be a Buffer');
if (typeof signatureHeader !== 'string' || !signatureHeader.startsWith('sha256=')) {
return false;
}
const receivedHex = signatureHeader.slice('sha256='.length);
if (!/^[a-f0-9]{64}$/.test(receivedHex)) return false;
const expected = createHmac('sha256', secret).update(rawBody).digest();
const received = Buffer.from(receivedHex, 'hex');
return received.length === expected.length && timingSafeEqual(received, expected);
}In Express, use a route-specific raw-body parser for this endpoint. Do not call JSON.stringify(req.body) and verify that reconstructed value.
Python example
import hashlib
import hmac
def verify_spacebrain_signature(raw_body: bytes, header: str | None, secret: str) -> bool:
prefix = "sha256="
if not header or not header.startswith(prefix):
return False
received = header[len(prefix):]
if len(received) != 64:
return False
expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(received, expected)Reject a missing, malformed, or mismatched signature before parsing or processing the payload. Return a non-2xx status and log only a safe delivery reference, time, and failure class—not the signature, secret, or full respondent body.
Handle repeated deliveries
A valid signature proves the body was signed with your secret. It doesn't prove the request is new: the header has no signed timestamp, and Spacebrain doesn't enforce a replay window. Your server needs to spot repeats itself.
- Extract a stable delivery, submission, or business-object identifier from the current test payload when one is provided.
- Record that identifier with a unique constraint (or another all-or-nothing check) before you create records or send messages.
- If the same identifier arrives again with the same intended operation, return success without creating a second record or message.
- If no stable identifier is available in the live payload, derive a receiver-owned deduplication key from the intended business operation and ask Spacebrain support to confirm the safest field before production launch.
- Do not deduplicate on respondent name alone.
Keep the retention window long enough for operational retries and manual reconciliation. Choose it from your own data and incident requirements; this guide does not promise a Spacebrain replay window.
Respond quickly and process safely
Check the content type, body size, signature, payload shape and whether you've seen this delivery before. Then return a successful 2xx response promptly and move long-running work to your own durable queue when possible.
A timeout or 5xx does not prove that the receiver made no change. Before replaying or manually testing again, inspect receiver logs and the downstream system for the same stable identifier.
In the form's Connect tab, open the destination's menu and choose Activity. Recent delivery activity can show In progress, Delivered, Needs attention, an attempt count and the latest HTTP status. Don't rely on a particular number of retries or retry schedule. If a delivery shows Needs attention, find out what your server did with it before you try again.
Test your endpoint
- Create the Webhook connection and store the one-time secret.
- Choose Send test from the connection's menu in Connect.
- Capture the raw body in a private test environment and verify the signature.
- Confirm a modified body fails verification.
- Confirm a missing/malformed signature fails.
- Send the same valid test twice and prove the receiver creates only one downstream outcome.
- Simulate a timeout or 5xx after the downstream action, then prove reconciliation prevents duplication.
- Submit a real internal Form response and match its response, delivery-history row, receiver record, and downstream outcome.
- Remove or label test data under the applicable retention policy.
Troubleshooting
| Symptom | Verify |
|---|---|
| Every signature fails | Correct connection secret, raw bytes rather than parsed JSON, UTF-8 handling, exact sha256= prefix, and lowercase hex digest |
| Test passes but live delivery fails | Route/body middleware, payload size, schema assumptions, environment secret, and receiver timeout |
| Duplicate downstream record | Your duplicate check, two connections pointing at the same endpoint, a timeout after your server already saved the record, and manual retries |
| Delivery shows Needs attention | Latest HTTP status, receiver failure class, downstream result, and duplicate risk before retry |
| Secret is lost or exposed | Disable/disconnect the destination, create a replacement, update the receiver, and rerun every test |
Next step
If deliveries still fail, work through Troubleshoot webhooks, Zapier, and Make.
Next step
Keep moving
Open the relevant Spacebrain screen or contact support if you need help.
Last updated on
Create, Publish, and Analyze Forms
Build a Spacebrain form, connect it to your CRM, style it, publish and embed it, send responses to other tools and read your results.
Landing pages and funnels
Build a landing page, a multi-step funnel or a quiz, starting from your goal, then embed forms, track conversions, publish and measure results.