How do I connect Typeform to Notion without Zapier?

Connect Typeform to Notion without Zapier using the native connection on a paid plan, or a webhook that writes to the Notion API. Steps and code inside.

Nookesh KarriOctober 30, 20266 min read
Light cover card with the title 'How do I connect Typeform to Notion without Zapier?' in dark text and a blue accent bar

You can connect Typeform to Notion without Zapier in two ways. On a paid plan, use the native Notion connection. On any plan, point a webhook at a small handler that creates the Notion page through the Notion API. The native route takes minutes; the webhook route takes an hour and gives you full control.

Two ways to connect Typeform to Notion without Zapier

The two Zapier-free routes are the vendor’s native Notion connection and a webhook you handle yourself. Most guides that rank for this swap Zapier for another automation tool such as Make, which is still a middleman with its own account and limits.

Native connection Webhook + Notion API
Plan needed A paid plan on the form tool Any plan that offers webhooks
Setup time Minutes About an hour
Who maintains it The form vendor You
Field mapping Mapped to question fields automatically Written in code
Retries if Notion fails Not described in the listing You choose, via status codes

Option 1: the native Notion connection

The native connection is the fastest Zapier-free route, as long as you pay for the form tool. As of October 2026, Typeform’s connection listing in Notion’s integration gallery says it “is only available on” paid plans.

The listing says it syncs responses into a Notion database, automatically mapped to the question fields. Setup is short:

  1. Start from the connection listing or from the form tool itself.
  2. Authorize your account.
  3. Select the form you want to connect.
  4. Pick the Notion database and check how each question was mapped.

The gallery marks this connection as made by the form vendor. Notion says connections that aren’t “Made by Notion” are not supported or maintained by Notion, so when a sync breaks, you call the vendor.

Option 2: a webhook that writes to the Notion API

A webhook sends each submission to a URL you own the moment it’s submitted, and your code creates the Notion page. The form tool’s Webhooks API docs (linked in the sources) describe this as sending “every submission straight to any URL”.

Before you write code:

  • Create a Notion internal connection. Notion’s authorization guide says you do this in the Developer portal and copy the API token from its Configuration tab. You must be a workspace owner.
  • Share the database with it. Open the database, click the ••• menu, choose Add connections and pick yours. Skip this and API requests return an error.
  • Somewhere to run code, such as a serverless function.

The handler below checks the signature, creates the page with the Create a page endpoint, and tells the sender to retry if Notion fails.

export default {
  async fetch(request, env) {
    const raw = await request.text();
    const key = await crypto.subtle.importKey(
      'raw', new TextEncoder().encode(env.WEBHOOK_SECRET),
      { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
    const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(raw));
    const expected = 'sha256=' + btoa(String.fromCharCode(...new Uint8Array(sig)));
    if (request.headers.get('typeform-signature') !== expected) {
      return new Response('bad signature', { status: 400 });
    }
    const res = await fetch('https://api.notion.com/v1/pages', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${env.NOTION_TOKEN}`,
        'Notion-Version': '2026-03-11',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        parent: { database_id: env.NOTION_DATABASE_ID },
        properties: toProperties(JSON.parse(raw)),
      }),
    });
    // A 503 makes the sender retry for hours instead of dropping the response.
    return res.ok ? new Response('ok') : new Response('notion error', { status: 503 });
  },
};

function toProperties(payload) {
  // Log one real delivery first, then map each answer to a property name.
  return { Name: { title: [{ text: { content: 'New response' } }] } };
}

Why the status code your handler returns matters

The status code your handler returns decides whether a failed response gets another chance. The webhook docs spell out the retry policy:

  • A 503 (or 429, 408, 423) makes the sender retry every 2-3 minutes for 10 hours.
  • 404 or 410 stops retries and disables the webhook immediately. Never return these for a temporary problem.
  • Any other error code gets retried on a back-off schedule, from 5 min up to 4 hrs.
  • No reply within 30 seconds counts as a failed delivery.

Notion’s request limits allow an average of 3 requests per second on most plans and answer with HTTP 429 when you go over. Pass that through as a 503, and a busy launch day delays responses instead of losing them.

Verify the signature before you trust a request

The signature check stops anyone who finds your URL from writing junk rows into Notion. The vendor’s webhook security docs describe the method: hash the entire raw payload with HMAC SHA-256 using your secret, base64-encode it, add the prefix sha256=, and compare it with the signature header.

Two mistakes cause most mismatches. Hash the raw body, not parsed and re-serialized JSON. And set the secret on the webhook first, through the Webhooks API, or there’s nothing to compare against.

Which route should you pick?

My take: if you already pay for the form tool, use the native connection and stop there. Build the webhook only when you need a property the mapping screen can’t fill, or control over retries.

The webhook route’s real cost is ownership. You maintain code, a secret and a Notion token, and Notion caps one rich text value at 2000 characters, so very long answers need splitting.

A builder that writes to Notion directly skips both the middleman and the code. nDBForms reads your database’s properties, builds matching fields, and writes each submission as a new row. It also keeps a copy of each response so failed Notion writes can be retried. See the integrations page for what it connects to.

If you’re still choosing tools, compare native Notion forms with this conversational builder, or read what makes the best Notion form builder. For the doc-style alternative, here’s how to connect a Tally form to Notion.

FAQ

Is the native Notion connection free?

No. Notion’s connection listing says the integration is only available on the form tool’s paid plans. On a free plan, the webhook route or a form builder that writes to Notion directly are the options.

Who maintains the native connection?

The form vendor does. Notion’s gallery marks it as made by the vendor, and Notion says connections not marked Made by Notion are not supported or maintained by Notion.

What happens if Notion is down when a response arrives?

With a webhook, return a 503 from your handler. The sender then retries every 2-3 minutes for 10 hours, so the response isn’t lost.

Do I need to be a Notion workspace owner?

For the webhook route, yes. Notion’s docs say you must be a workspace owner to create an internal connection and get its API token.

Sources