Notion forms: send responses to a database
Every response becomes a page in your Notion database, as it arrives. Setup is a token, a database ID, and one step inside Notion that almost everyone misses the first time.
What arrives in Notion
Each response becomes one page in the database you nominate, with every answer written into the column of the same name. Delivery happens as the response arrives, in a five-second budget, with no retries — the response is stored in Seagit either way, so a Notion outage never costs you the submission itself.
Notion delivery is a paid feature, alongside Slack, Discord and Airtable. Webhooks stay on the free plan. See plans.
Should you use Notion’s own forms instead?
Often, yes — and it is worth saying so before you set any of this up. Notion has a built-in forms feature that collects straight into a database with no token, no ID and no connection step. If a response only ever needs to land in Notion, native Notion forms are the shorter path and nothing here beats being inside the product already.
This destination is for the case that one does not cover: the same response has to reach more than one place. A Seagit form can write a Notion page, POST to your own endpoint, post into Slack and add an Airtable row from a single submission, and it can be embedded on your own site with a domain allow-list. If your Notion database is one destination among several rather than the only one, that is what this is for.
How do I create a database in Notion?
Responses land in a database, not a page, so you need one first. If you already have a database to write into, skip to the token below.
- Open app.notion.com/new to start a new page, and under Get started with choose Table. Inside an existing page, the slash command
/databasedoes the same thing. - Name it, then add one column per question you want to keep. The column names are what answers are matched against, and the column types decide what is accepted — so an email question wants an Email column, a number question a Number column, and everything else can stay as Text.
- Every database starts with one title column (usually called Name). Point your most identifying question at it — a name or an email — because that is what shows in the database list.
- Copy the database ID from its URL: the 32-character hex segment, before the
?v=. Take it from the database’s own page, not from a page inside it.
Notion’s own guide to creating and using databases covers views, filters and property types in more depth.
Which Notion token do I need?
Notion offers two kinds, both starting ntn_, and the choice decides whether you need the sharing step below. This is the part most setup guides skip.
| Personal access token | Internal connection | |
|---|---|---|
| Acts as | You | Its own bot user |
| Needs the database connected to it | No — it inherits your own access | Yes — via ••• → Add connections |
| Who can create one | Any member, unless the workspace restricts it | Workspace Owners only |
| If you leave the workspace | The token loses access, and delivery stops | Keeps working |
For one person’s form, use a personal access token. It skips the step that causes almost every failed Notion setup. For a form the team owns and that should outlive any one account, use an internal connection and do the sharing step deliberately.
How do I connect a form to a Notion database?
- Create the token. Open the Developer portal → Personal access tokens, select New token, give it a name and the Notion API capability, and press Create token. Copy it — it is shown once.
For a team-owned setup instead, use Internal connections → Create a new connection, which needs Workspace Owner. - Only if you chose an internal connection: open the database in Notion, click ••• at the top right, then Add connections at the bottom of the menu, and pick your connection. A personal access token does not need this.
- In the form editor, open Settings → Storage, choose Notion, paste the token and the database ID, and press Check connection.
The check is the point
Check connection reads your database, lists its real column names back, and compares them against your question labels. If none of them match it fails, because a submission would deliver nothing — and if only some match, it names the ones that will be skipped. It is the difference between finding a mismatch now and finding it after a respondent’s answer has already been dropped.
Why Notion says the database was not found
This is the failure a completely correct setup still hits, and it has exactly one cause worth checking first. An internal connection token does not grant access to a workspace. It operates as its own bot user, and it reaches only the pages and databases that someone has explicitly connected it to, one at a time, from inside Notion.
Until that is done, Notion answers 404 object_not_found — and its message says the database could not be found. That wording is accurate from Notion’s point of view and misleading from yours: the ID is fine, the token is fine, and the thing to fix is a menu two clicks away. It is the exact counterpart of Slack’s not_in_channel, where a valid bot token still cannot post to a channel it has not joined.
If you are using an internal connection
Open the database in Notion → ••• (top right) → Add connections → pick your connection. Access is inherited, so connecting a parent page covers the databases inside it — but a database elsewhere in the workspace is not included. Notion’s guide to connections has the screenshots.
A personal access token does not need any of that — it uses your own page permissions, so it can already see anything you can. If you are holding one and still getting a 404, the database ID is genuinely wrong, or the database sits in a different workspace from the one the token belongs to.
Question labels must match your column names
Notion addresses properties by name and refuses the whole page when one key is unknown — so a form asking “Full name” against a database with a “Name” column would lose every answer in the page, not just that one.
Rather than let that happen, an answer whose label matches no column is left out and named in the delivery log; the rest of the page lands as normal. Check connection compares your question labels against the real column names and refuses to pass when nothing matches, so a database that would receive nothing fails the check rather than looking connected.
Once linked, names stop mattering
Matching by name is only the starting point. When you press Create missing columns, each question is bound to its column’s property ID — the one handle a Notion column has that never changes. After that you can rename the question here, or rename the column in Notion, and answers keep arriving in the right place. Two questions that happen to share a label also get two real columns rather than one, so neither overwrites the other.
A brand-new database has one column
Notion creates a database with a single Name column. If you point a form at one and change nothing else, every answer is skipped and no page is created — the connection is fine, there is simply nowhere for the answers to go.
Create missing columns, next to Check connection in the destination settings, adds one column per question in a single press, typed to match: an Email question becomes an Email column, a number or rating becomes a Number, a dropdown becomes a Select. It changes your Notion database, so it only ever runs when you press it — delivery never alters your schema on its own.
It also links each question to the column it created, by Notion’s property ID. That link is what lets you rename either side afterwards. Save the form once it is done, or the link is lost when the settings close.
How Notion column types are handled
Notion properties are typed, which is the one real difference from Airtable, and a rejected property costs the entire page rather than the one answer. So the database schema is read first — cached for five minutes — and each answer is shaped to the type the column actually declares.
Which types are strict is worth knowing, because it is not uniform. We round-tripped every shape against a real Notion database on 3 September 2026:
| Sending a bare string into… | Notion’s answer |
|---|---|
| Email, URL, Phone | Accepted, and stored as typed |
| Text (rich_text) | Rejected — needs the array form |
| Number | Rejected — needs an actual number |
| Select, with a comma in the name | Rejected — commas are not allowed in an option |
| A column name that does not exist | Rejected — and the whole page is lost |
The two that bite are Text and Number — which are also the two commonest column types a form writes into. Every shape in the table below is the one we send, and each was confirmed to land.
| Notion column type | What is sent |
|---|---|
| Title | The answer, truncated at 2,000 characters |
| Text | The answer, truncated at 2,000 characters |
| Number | Sent only if the answer parses as a number, else skipped |
| Checkbox | True for true, yes, on, 1; false otherwise |
| Select | One option. Commas are removed — Notion rejects an option name containing one |
| Multi-select | One option per selected answer |
| Date | Sent only if the answer parses as a date, else skipped |
| Email, URL, Phone | The answer as typed |
| People, Relation, Files, Status | Skipped — each needs an ID we do not have |
| Formula, Rollup, Created/Edited time or by | Skipped — Notion computes these and rejects a write |
Two things that never arrive
- Uploaded files. A file exists in a response as a base64 data URI; a Notion files property needs a fetchable URL. The file stays on the response and in the CSV export.
- Anything Notion generates. Formula, rollup and the created/last-edited columns are computed by Notion, and setting one is a 400 that would lose the page.
What each Notion error means
| Response | What to do |
|---|---|
404 object_not_found | On an internal connection: the database is not connected to it — ••• → Add connections. On a personal access token: the database ID is wrong, or it is in another workspace. |
401 unauthorized | The token is wrong or was regenerated. Copy it again from app.notion.com/developers. |
403 restricted_resource | The integration lacks insert-content capability, or does not have permission for this database. |
400 validation_error | A property name or type does not match. Compare your field labels against the column names. |
400 missing_version | Only reachable from your own code — every Notion request must carry a Notion-Version header. |
429 rate_limited | Notion throttled the request and the page was not created. Deliveries are not retried. |
Known limits
- One database per form. Add a second destination for a second database, or a webhook to fan out yourself.
- A database with several data sources — Notion’s newer multi-source databases — receives the page in its default source. If you rely on a specific one, use a webhook.
- No retries. One attempt, five seconds. The response is always stored in Seagit regardless.
- The page has properties, not content. Answers land in columns; nothing is written into the page body.
- The title column gets the submission time unless a question is bound to it, so responses are told apart in Notion’s list view rather than all reading “Untitled”.
Common questions
- Why does Notion say my database was not found when the ID is right?
- Almost always because you are using an internal connection token and the database has not been connected to it. An internal connection operates as its own bot user and reaches only what someone has explicitly connected it to, from the database’s ••• menu → Add connections. Until that is done Notion answers 404 object_not_found for a perfectly valid token, and the wording sends people to re-check an ID that was never wrong. A personal access token avoids this entirely, because it uses your own page permissions — if one of those gets a 404, the ID really is wrong or the database is in another workspace. Pressing Check connection reports whichever it is in those words rather than as "not found".
- Where do I find my Notion integration token?
- In the Notion Developer portal, at app.notion.com/developers. There are two kinds and the choice matters: a personal access token (New token → name → Notion API capability) acts as you and needs no sharing step, which makes it the right one for a form you own; an internal connection acts as its own bot user, needs Workspace Owner to create, and requires each database to be connected to it — but it keeps working if you leave the workspace. Both start ntn_ and are shown once. Treat either as a credential: anyone holding it can write to everything it can reach.
- Where is the database ID in a Notion URL?
- It is the 32-character hex segment. In notion.so/myspace/a8aec43384f447ed84390e8e42c2e089?v=… the ID is a8aec43384f447ed84390e8e42c2e089, and the part after ?v= is a view, not the database. Copy it from the database’s own page rather than from a page inside it — a page ID looks identical and produces a database that is readable but has no columns, which the connection check reports rather than letting it fail on the first real submission.
- Why is one of my Notion columns always empty?
- The question label and the column name do not match exactly. Notion addresses properties by name and rejects the whole page when one key is unknown, so an answer with no column of its name is left out and named in the delivery log rather than losing every other answer with it. Press Check connection: it lists the database’s real column names. Capitalisation and trailing spaces count.
- Do I have to match Notion column types as well as names?
- No — that part is handled for you. Notion properties are typed, so an email column needs {"email": "…"} and rejects a bare string with a 400 that loses the entire page. The delivery reads your database schema first and shapes each answer to the type the column actually declares: title, rich text, number, checkbox, select, multi-select, date, email, URL and phone. An answer that cannot be shaped confidently — a number column given "about forty" — is skipped and named rather than sent hopefully.
- Do I have to create the Notion columns by hand?
- No. Press Create missing columns in the destination settings and every question that has no column gets one, named after the question and typed to match — Email for an email question, Number for a number or rating, Select for a dropdown, Multi-select for checkboxes, Date for a date, and Text for everything else. It only runs when you press it: delivery never changes your database schema on its own, because a respondent submitting a form should not be able to alter your workspace. File questions are skipped, since their answers are never delivered anyway. Save the form afterwards — the link between your questions and the columns is stored on the form, and closing without saving loses it.
- Can I rename a question, or a Notion column, without breaking delivery?
- Yes, once you have pressed Create missing columns. That records each question against its column’s Notion property ID, which is the only handle a column has that survives a rename — so you can rename the question in Seagit, or the column in Notion, and answers keep landing in the same place. Before that link exists, matching is on the name alone, and renaming either side does break it: Notion rejects a write to a column name that no longer exists, and that rejection costs the whole page rather than the one answer.
- Two of my questions have the same name. What happens?
- They get two separate columns — the second is numbered, so "Short Text" and "Short Text 2" — and each question stays bound to its own. This matters because the form editor names a new field after its type, so a form with two short-text questions really does start with two questions called "Short Text". Sharing one column would mean the second answer overwrote the first on every submission.
- Why do my responses show as "Untitled" in Notion?
- Because nothing is writing to the database’s title column. Notion leads its list view with the title, so a database whose title column is called "Name" and whose form has no question labelled "Name" ends up as a list of rows all reading Untitled. When no question maps to the title, the submission time is written instead — "Response — 3 Sep 2026, 02:54" — which is real metadata rather than an invented answer. To use a real answer instead, bind a question to the title column: rename it to match, or press Create missing columns and rename the column in Notion afterwards.
- Do file uploads arrive in Notion?
- No. A file lives inside a Seagit response as a base64 data URI, and a Notion files property needs a publicly fetchable URL, so there is nothing to hand it. File answers are skipped and named in the delivery log rather than written as a 200 KB string into a text column. The file is still on the response in your dashboard and in the CSV export.
- Is Notion delivery on the free plan?
- No. Notion, Slack, Discord and Airtable are all on the paid plans — Basic at $20 a month and above. Webhooks are free, and a webhook can reach Notion through your own code if you would rather not pay for the built-in one. Everything else about the form, including collecting and exporting the responses, works on the free plan.