Signature Verification
Every webhook request includes an X-FormTorch-Signature header containing an HMAC-SHA256 signature of the raw request body, signed with your endpoint’s signing secret.
Always verify this signature before processing any webhook payload.
How it works
- Formtorch serializes the payload to JSON
- It signs the raw JSON string with HMAC-SHA256 using your signing secret
- The result is sent as
sha256=<hex>in theX-FormTorch-Signatureheader - Your server recomputes the expected signature and compares it to the header
Verification examples
Node.js
import { createHmac, timingSafeEqual } from "crypto";
export function verifyWebhookSignature(
body: string,
signature: string,
secret: string
): boolean {
const expected = `sha256=${createHmac("sha256", secret)
.update(body)
.digest("hex")}`;
// Buffer lengths must match before timingSafeEqual — mismatched lengths throw
const a = Buffer.from(signature);
const b = Buffer.from(expected);
if (a.length !== b.length) return false;
return timingSafeEqual(a, b);
}Usage in a Next.js route handler:
import { verifyWebhookSignature } from "@/lib/verify-webhook";
export async function POST(req: Request) {
const body = await req.text(); // read raw body BEFORE JSON.parse
const signature = req.headers.get("x-formtorch-signature") ?? "";
if (
!verifyWebhookSignature(
body,
signature,
process.env.FORMTORCH_WEBHOOK_SECRET!
)
) {
return new Response("Unauthorized", { status: 401 });
}
const event = JSON.parse(body);
if (event.event === "form.submitted") {
const { id, data } = event.submission;
console.log(`New submission ${id}:`, data);
// your logic here
}
return new Response("OK");
}Read the raw body first
Signature verification requires the raw request body string, not the parsed JSON object. If you parse first, the string representation may differ and the check will fail.
In Express, use express.raw({ type: 'application/json' }) on the webhook
route instead of express.json(). In Next.js App Router, call req.text()
before JSON.parse().
Finding your signing secret
Go to Settings → Webhooks in the dashboard. The signing secret is shown exactly once when you create an endpoint. If you lose it, delete the endpoint and create a new one — the secret cannot be recovered.
Other request headers
In addition to the signature, each request includes:
| Header | Value |
|---|---|
X-FormTorch-Event | Event name, e.g. form.submitted |
X-FormTorch-Delivery | Unique delivery ID (matches delivery_id in the body) |
User-Agent | FormTorch-Webhooks/1.0 |
You can use X-FormTorch-Event to route events without parsing the body first.