send.webforms.toRead the docs →

how-tonotion

Turn form submissions into Notion database rows

Your contact form becomes the intake end of a Notion pipeline your team already lives in.

WebForms5 min read

A submission that lands in an inbox is a message; the same submission in a Notion database is a row with a status. It can be assigned, filtered, given a Kanban lane, and seen by the whole team without forwarding. If your team already runs on Notion, sending forms anywhere else means someone copies them in by hand - and eventually stops.

Set up the database and integration

  1. Create a Notion database with properties matching your form: Name (title), Email (email), Message (text), plus workflow properties like Status.
  2. Create an internal integration at notion.so/my-integrations and copy the secret.
  3. Share the database with the integration (Connections menu on the database page) - forgetting this is the classic silent failure; the API returns 404 as if the database does not exist.

The relay

Until the native Notion adapter ships, a small serverless function receives the WebForms webhook and creates the page. This is the whole thing:

api/notion-relay.js
export default async function handler(req, res) {
  const body = req.body;

  await fetch("https://api.notion.com/v1/pages", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.NOTION_TOKEN}`,
      "Notion-Version": "2022-06-28",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      parent: { database_id: process.env.NOTION_DATABASE_ID },
      properties: {
        Name: { title: [{ text: { content: body.data.name } }] },
        Email: { email: body.data.email },
        Message: {
          rich_text: [{ text: { content: body.data.message ?? "" } }],
        },
      },
    }),
  });

  res.status(200).json({ ok: true });
}

Deploy it anywhere that runs a function, add the URL as a webhook destination, and submit once. The row appears with your workflow properties empty, ready for triage. Because spam is filtered before delivery, the database only accumulates real enquiries - a lead tracker full of junk rows stops being looked at within a week, which quietly kills the whole system.

Verify the webhook signature in the relay if the function URL is guessable; the delivery includes a signed header for exactly this. And set the Notion token as an environment variable, never in the code - the snippet above assumes both.

Get your endpoint

Free for 300 submissions a month. No card required.

Start free