Notifications and webhooks
Send each form's new submissions to email, Slack, Discord, Teams, Telegram, WhatsApp, Google Sheets or a signed webhook, with retries.
SiteBackend can tell you about every new submission on the channels your team already uses. Notifications are set up per form: open a form and go to its Notifications tab.
Which channels are supported?
| Channel | What you need |
|---|---|
| One or more email addresses. | |
| Slack | An incoming webhook URL. |
| Discord | A channel webhook URL (Channel settings → Integrations → Webhooks). |
| Microsoft Teams | An incoming webhook URL for the channel. |
| Telegram | A bot token from @BotFather and a chat ID. |
| A WhatsApp Cloud API access token, phone number ID and recipient number. | |
| Google Sheets | A spreadsheet shared with SiteBackend's service account; each submission becomes a row. |
| Webhook | Any HTTPS URL. Requests can be signed so you can verify them. |
Secrets such as tokens and webhook URLs are stored encrypted and never shown again after saving.
How do I add a channel?
In Form → Notifications, click Add channel, pick a type and fill in its details. SiteBackend sends a test message straight away and marks the channel Verified when it arrives. Each channel has Test, Edit, delete and an on/off switch, and each form can have up to 10 channels. Editing a channel's destination clears Verified until you test it again.
Every new submission that reaches the form's inbox is sent to each of its active channels. Spam never is. To notify the same Slack channel from several forms, add it to each form.
What about the monthly limit?
On the Free plan, SiteBackend emails the workspace's owners and admins at 80% of the monthly limit and again when it's reached. These alerts aren't tied to a form, so they don't use form channels. Submissions past the limit are saved but hidden, and don't send notifications. See plans and limits.
What about new testimonials?
Testimonials don't use form channels. Owners and admins get an email for each new testimonial, with a link to approve it. Turn it off with Email me new testimonials on the site's Testimonials → Collect page. See collecting testimonials.
Can the person who submitted get an email too?
Yes, on Pro: turn on Auto-reply in the form's settings. See forms.
What if a channel is down?
Deliveries are retried automatically, up to 8 attempts over about 11 hours. Each form's Notifications tab has a delivery log with every delivery, its status and the last error. Failed deliveries can also be retried by hand from there or from the submission.
Webhooks
Webhook channels send a POST (or PUT) with a JSON body:
{
"id": "delivery id",
"event": "SUBMISSION_CREATED",
"createdAt": "2026-09-26T10:00:00.000Z",
"data": {
"event": "SUBMISSION_CREATED",
"form": { "id": "…", "publicId": "…", "name": "Contact form" },
"submission": { "id": "…", "data": { "email": "[email protected]", "message": "Hi" }, "createdAt": "…" }
}
}Every request includes these headers:
| Header | Value |
|---|---|
x-sitebackend-event | SUBMISSION_CREATED |
x-sitebackend-delivery | A unique delivery ID; use it to ignore duplicates. |
x-sitebackend-timestamp | Unix time in seconds. |
x-sitebackend-signature | t=<timestamp>,v1=<hex>, when the channel has a signing secret. |
Your endpoint should respond with a 2xx status. Anything else is retried.
How do I verify a webhook signature?
Compute an HMAC-SHA256 of `${timestamp}.${rawBody}` with your signing secret, compare it to v1, and reject old timestamps:
import crypto from "node:crypto";
export function verify(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300; // 5 minutes
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}