Signed webhooks with retries
Add an endpoint URL and Formwire posts every new submission to it as JSON. A signature header lets you verify each request came from us, retries cover a flaky endpoint, and the delivery log shows what happened.
A signed JSON payload
Each request carries a Formwire-Signature header: a timestamp and an HMAC-SHA256 of the body, keyed with the webhook's secret. Recompute it on your side and reject anything that doesn't match or is more than a few minutes old.
Retries that give up gracefully
A non-2xx answer, a timeout after 10 seconds or a redirect counts as a failure. Formwire retries up to six times, after 1, 5, 15, 60 and 240 minutes, then marks the delivery as gave up.
- Five deliveries in a row that fail every retry switch the webhook off and email you.
- Retry a delivery by hand from the submission's page.
Test before you trust it
Send a test payload from the dashboard and see the response status straight away. Test deliveries are marked test: true in the body so your endpoint can tell them apart.
Safe by default
Webhook URLs must be public HTTPS addresses: private networks and local hosts are refused. A webhook added with an API key or through MCP waits for someone to confirm it in the dashboard before it receives anything.
- Rotate the signing secret any time.
The payload your endpoint receives
One JSON object per submission, with the fields exactly as your form posted them.
- 1
Open the form's Settings, then Integrations, and add your endpoint URL. Copy the signing secret shown once.
- 2
Verify Formwire-Signature on each request: HMAC-SHA256 of `<t>.<raw body>` with the secret.
- 3
Answer with any 2xx within 10 seconds. Do the slow work after you've answered.
- 4
Watch the delivery log on each submission, or send a test from the dashboard.
POST https://hooks.acme.com/formwire
Formwire-Signature: t=1760400000,v1=5f1a…c3
Formwire-Delivery-Id: dlv_01J9…
Formwire-Event: submission.created
{
"id": "dlv_01J9…",
"type": "submission.created",
"created_at": "2026-10-14T09:20:00.000Z",
"data": {
"submission_id": "sub_01J9…",
"form_id": "frm_01J8…",
"form_name": "Contact",
"source": "form",
"created_at": "2026-10-14T09:19:58.000Z",
"page_url": "https://acme.com/contact",
"fields": { "name": "Ada", "email": "ada@example.com", "message": "Hi" }
}
} Questions about webhooks
How do I verify the signature?
Take the t value from the Formwire-Signature header, compute HMAC-SHA256 of `<t>.<raw body>` with your webhook's secret, and compare it with v1 in constant time. Reject requests whose t is more than a few minutes old.
What counts as a failure?
Any status outside 200 to 299, no answer within 10 seconds, or a redirect. Formwire never follows redirects; point the webhook at the final URL.
Will the same submission be sent twice?
A retry sends the same payload with the same id and Formwire-Delivery-Id, so you can de-duplicate on it. A new submission always has a new id.
Can I post to a server on my own network?
No. Webhook URLs must resolve to public addresses, so Formwire can't be used to reach private networks. Use a tunnel or a public relay during development.
Goes well with
-
Integrations
Signed webhooks to any endpoint, Slack, Discord, Telegram, Google Sheets, Zapier, Make, n8n, Notion and Airtable apps, a read API and an MCP server for Claude Code and Cursor.
-
Submission inbox
A dashboard inbox for each form with search, folders, bulk actions, CSV export and a read API, so you never dig through email to find a submission.
-
Email notifications
Each form post becomes an email within seconds, with the visitor as Reply-To. Send to up to 10 recipients on Pro. Retries and a delivery log on every send.