Send HTML form submissions to Slack, Discord, or Zapier without a backend
Three patterns for piping a static-site contact form into chat tools, automation platforms, and your own internal services. With copy-pasteable examples.
A form notification email is great until you have a team. Then you want submissions in your team chat. Then you want them automatically creating tickets, triggering Zapier flows, and pinging your internal API. The good news: you don't need to write a backend for any of it. The trick is webhooks.
A webhook is just an HTTP POST your form service makes to a URL of your choice every time a submission comes in. That's the entire mental model.
Three patterns, in increasing order of effort
- Direct to a chat tool. Slack, Discord, and Microsoft Teams all expose “incoming webhook” URLs that accept a JSON payload and post it as a message. No code, just a URL.
- To an automation hub. Zapier, Make, n8n, Pipedream. Point the webhook at one of these and you can fan out to anything (CRM, spreadsheet, ticket system, custom email) without writing code.
- To your own backend. If you already have a service running, expose a webhook receiver endpoint and do whatever bespoke logic you need.
Pattern 1a: Submissions to Slack
Slack's incoming webhook accepts a payload like this:
{
"text": "New contact form submission",
"blocks": [
{ "type": "header", "text": { "type": "plain_text", "text": "📨 New contact" } },
{ "type": "section", "text": { "type": "mrkdwn", "text": "*From:* {{name}} <{{email}}>" } },
{ "type": "section", "text": { "type": "mrkdwn", "text": "{{message}}" } }
]
}A good form service will let you write a template like this directly and substitute in {{field}} values from each submission at delivery time. Configure once, every future submission formats itself.
Pattern 1b: Submissions to Discord
Discord webhooks use a different shape (embeds rather than blocks) but the principle is identical:
{
"embeds": [{
"title": "New form submission",
"color": 16711935,
"fields": [
{ "name": "Name", "value": "{{name}}", "inline": true },
{ "name": "Email", "value": "{{email}}", "inline": true },
{ "name": "Message", "value": "{{message}}" }
]
}]
}Pattern 2: Submissions through Zapier (or Make / n8n)
Automation platforms accept the raw submission JSON and let you chain whatever happens next visually: “new webhook → create HubSpot contact → post to #sales → email confirmation”.
Configure the webhook to send the unmodified submission as JSON (the “raw” payload mode in most form services), grab the URL Zapier gives you, and you're done. You don't need to write a template; the platform handles field mapping in its own UI.
Pattern 3: Submissions to your own backend
Sometimes you do want code. Maybe the form is a job application and you want to score it, or it's a lead and you want to enrich it with Clearbit before routing. Expose a webhook receiver:
export async function POST(req: Request) {
// Verify the webhook came from us. Most services sign requests.
const signature = req.headers.get('x-saveform-signature');
if (!isValidSignature(signature, await req.text())) {
return new Response('Unauthorized', { status: 401 });
}
const body = await req.json();
// body.data is your form fields; body.formName, body.submittedAt, etc.
await routeSubmission(body);
return Response.json({ ok: true });
}Multiple destinations are normal
Most teams end up with two or three webhooks per form: one to Slack for awareness, one to Zapier for the routing logic, one to their own backend for high-stakes processing. A good form service fans the same submission out to all of them in parallel and gives you a per-destination delivery log so you can see which one succeeded.
Three pitfalls to avoid
- Forwarding spam. By default your webhooks should not fire for spam-flagged submissions. You don't want bot junk in your team chat.
- Synchronous waits. If the webhook receiver is slow, the form submitter shouldn't feel it. Webhook delivery should run after the form's success response is already on its way.
- Silent failures. Webhook deliveries fail. Receivers go down, certs expire, Slack blocks you. You need a delivery log to see when this happens, and a way to retry the misses manually.
For the gory details on how SaveForm fans out, signs, and logs deliveries, see the webhooks docs.
One submission, every destination
SaveForm fans every submission out to as many webhooks as you want: Slack, Discord, Zapier, your own backend. With templates, auth, and a per-destination delivery log.