Send form submissions to email and a webhook
Email each new lead to your team and post it as JSON to your own endpoint, with the exact payload, how to check it’s really from 1DB, and what happens when your endpoint is down.
Updated
Once a site’s leads are in 1DB, each form decides who hears about them. There are two built-in ways: an email to your team, and a webhook to a URL you control. Both are set per form, so the quote form can alert sales while the careers form goes to someone else.
Email alerts
Open the form in the dashboard, go to Notifications, turn on email, and list up to ten addresses. Every new lead sends one email to each, with the form’s name and every field the visitor filled in.
From the command line, the same setting is:
npx @1db/cli notify quote-request --emails sales@northfieldpallets.com,dana@northfieldpallets.comA submission that 1DB recognizes as a repeat of one it already has (see stopping duplicate submissions) doesn’t send a second email.
Webhooks
Go to the form’s Webhook tab, turn it on and paste your URL. Use an https:// address. For every new lead, 1DB sends a POST with this body:
{
"event": "lead.created",
"timestamp": "2026-10-27T14:03:11.402Z",
"data": {
"leadId": "k57a3bx9r2q8m1n0c4d6e8f0",
"formId": "j97f2kq8w1x3m5n7p9r1t3v5",
"formName": "Quote request",
"isDuplicate": false,
"fields": {
"first_name": "Dana",
"email": "dana@northfieldsupply.com",
"quantity": 300,
"message": "Need 48x40s delivered to Columbus"
},
"metadata": {
"ip": "203.0.113.24",
"userAgent": "Mozilla/5.0 …",
"createdAt": 1793110991402
}
}
}fields holds exactly what the form sent, under the names it used. isDuplicate is currently always false; a repeat of the same submission never reaches your webhook at all.
To set it from the command line:
npx @1db/cli notify quote-request --webhook https://hooks.northfieldpallets.com/leadsCheck the request is really from 1DB
Anyone who learns your webhook URL can post to it. Give the webhook a secret header and reject requests without it. Headers are set through the agent API or Claude Code’s 1DB tools (set_form_notifications), and stored with the form; changing the URL clears them, so a secret meant for one endpoint is never sent to another.
// app/api/leads-webhook/route.ts
export async function POST(request: Request) {
if (request.headers.get("x-webhook-secret") !== process.env.LEADS_WEBHOOK_SECRET) {
return new Response("Forbidden", { status: 403 });
}
const { event, data } = await request.json();
if (event !== "lead.created") return new Response(null, { status: 204 });
await saveToWarehouse({ id: data.leadId, ...data.fields });
return new Response(null, { status: 204 });
}When your endpoint is down
1DB sends each webhook once. If your endpoint answers with an error or doesn’t answer, the failure is logged in the organization’s activity with the status code or error, and the lead itself is safe in 1DB. It isn’t retried, so if you need every lead downstream:
- answer quickly with a
2xxand do slow work afterwards, in a queue - key your writes on
leadId, so replaying a lead twice is harmless - for leads missed during an outage, read them back with
npx @1db/cli leads <site>or the agent API and replay them
If you need guaranteed delivery with retries to another company’s API, use a forwarding rule instead: those are paced and retried when the destination is down.
Slack and other chat tools
Slack’s incoming webhooks expect their own message format, so point the 1DB webhook at a small function of your own that reshapes the payload:
await fetch(process.env.SLACK_WEBHOOK_URL!, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: `New ${data.formName}: ${data.fields.first_name ?? ""} <${data.fields.email}>`,
}),
});Next: create each lead in HubSpot too, or record which campaign it came from.