Webhooks
Webhooks let Formtorch push submission data to your server the moment it arrives. For each event you subscribe to, Formtorch sends a signed POST request to your endpoint.
Webhooks are available on Starter and Pro plans.
Want a full implementation walkthrough? See Webhook Processing for working examples in Next.js and Express.
Setting up a webhook
Webhook endpoints are workspace-level: one endpoint can receive events from all your forms.
Open Webhooks settings
Go to Settings → Webhooks in the dashboard . Click Add Endpoint.
Enter your URL and choose events
Enter your endpoint URL (must be https://). Select the events you want to receive:
| Event | When it fires |
|---|---|
form.submitted | A real, non-spam submission is received |
form.spam | TorchWarden flags a submission as spam |
form.test | A test submission is sent (_test=true or via the Send Test button) |
You can subscribe to one, two, or all three.
Copy the signing secret
After saving, the signing secret is shown exactly once. Copy it and store it as an environment variable. If you lose it, delete the endpoint and create a new one.
That’s it. Formtorch begins delivering events to your endpoint immediately.
Payload
Every event uses the same JSON envelope:
{
"event": "form.submitted",
"timestamp": "2025-03-15T14:30:00.000Z",
"delivery_id": "d3f456abc789",
"form": {
"id": "a1b2c3d4e5",
"name": "Contact Form"
},
"submission": {
"id": "x9y8z7w6v5",
"created_at": "2025-03-15T14:30:00.000Z",
"data": {
"name": "Alex",
"email": "alex@example.com",
"message": "Hello there"
},
"is_test": false,
"is_spam": false
}
}submission.data contains all submitted fields except reserved _* fields (_redirect, _honeypot, _formName, _test), which are stripped before delivery.
Request headers
Each webhook request includes these headers:
| Header | Description |
|---|---|
Content-Type | application/json |
X-FormTorch-Signature | HMAC-SHA256 signature: sha256=<hex> |
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 |
Signature verification
Always verify X-FormTorch-Signature before processing a payload. This confirms the request came from Formtorch and the body was not tampered with.
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")}`;
const a = Buffer.from(signature);
const b = Buffer.from(expected);
if (a.length !== b.length) return false;
return timingSafeEqual(a, b);
}Read the raw request body as a string before parsing JSON. Parsing first changes the string representation and breaks the check.
For full examples in Node.js, Python, and PHP, see Signature Verification.
Retry behavior
Your endpoint must respond within 30 seconds with a 2xx status. If it returns a non-2xx response or times out, Formtorch retries the delivery automatically with exponential backoff, up to 6 total attempts (1 initial + 5 retries).
After all attempts are exhausted, the delivery is marked failed. You can retry it manually from the delivery log.
Return 200 OK immediately and process the payload asynchronously if your
handler takes more than a few seconds to complete.
Delivery log and manual retry
The delivery log for each endpoint is visible in Settings → Webhooks. It shows the status, HTTP response code, and response time for recent deliveries. Failed deliveries can be retried individually with the Retry button.
Testing your endpoint
Use the Send Test button on any endpoint to fire a form.test event immediately without submitting an actual form. This is useful for verifying your integration and signature verification before going live.
Pausing an endpoint
Click the toggle on any endpoint to pause it. Paused endpoints do not receive deliveries. Queued jobs for a paused endpoint are dropped, not buffered. Unpause to resume delivery.