Skip to main content

Notion Integration

5 min readStableBeginner

Connect a Notion database and every vote from the "Was this helpful?" widget lands in it as a row: the page path, a thumbs up or down, the time it came in, and an optional comment when a reader explains why a page fell short. You read feedback right in Notion, filter it, and share it with your team. This page covers creating the integration and the database, then wiring both into OwnDocs.

Why an integration at all? By default, OwnDocs writes votes to a JSONL file on your server. That works on any normal Node host. It doesn't work on Vercel, where the filesystem is read-only, so the write fails and your reader sees an error. Notion is the fix for those hosts.

Prerequisites

A Notion account. The free plan covers everything here. You also need two values, and the steps below produce both:

NOTION_API_KEYstringrequired

The internal integration token from Notion. It starts with ntn_.

NOTION_DATABASE_IDstringrequired

The database that receives the votes. A 32-character ID, copied from the database URL.

Pick one of three ways to get them. The third is optional and uses the bundled script.

Steps

Option 1: Click through Notion

No terminal needed. About five minutes.

  1. Create the integration

    Open notion.so/profile/integrations and choose New integration. Name it something like docs-feedback, pick your workspace, and save. Copy the Internal Integration Secret: it starts with ntn_. That token is your NOTION_API_KEY. If that link ever moves, Notion's developer portal at notion.so/developers/tokens is the fallback from their official quick-start guide.

  2. Create the database

    Add a new page in Notion and choose Table. Name it Docs Feedback (any name works). Then give it exactly these four properties:

    Pathtitlerequired

    The page's URL path, like /integrations/notion. Notion adds a title property for you: rename it to Path if it says something else.

    Helpfulcheckboxrequired

    Checked for a thumbs up, empty for a thumbs down.

    Submitteddaterequired

    When the vote came in.

    Commentrich_text

    The optional text a reader leaves after a thumbs down. Without it, votes still save; only comments are skipped.

    Match the names exactly, capital first letter included. OwnDocs looks them up by name.

  3. Share the database with the integration

    Open the database, click the ⋯ menu at the top right, choose Connections, and pick the integration from step 1. This screen has no direct URL of its own: the Connections menu lives on each page and database, two clicks from the database you just made. Until you do this, the token sees nothing. Notion keeps bots out of your content by default.

  4. Copy the database ID

    Open the database as a full page and look at the URL. The 32 characters between the last dash and the ?v= part are the ID, highlighted here:

    Plain Text
    Plain Text
    https://www.notion.so/myworkspace/Docs-Feedback-218fb8926f02e83e52a542a8869bb279?v=594fa6dddfef38df9e92acffe2f1f1e1

    That's your NOTION_DATABASE_ID.

Option 2: One API call

Faster if you live in a terminal. A single call to Notion's application programming interface (API) creates the database, but a bot can only create one inside a page that's already shared with it. Share any page with your integration (step 3 above), then run the command below.

Export your token and the parent page's ID, the 32 characters at the end of its URL:

Terminal
Bash
export NOTION_API_KEY="ntn_your-token"
export PARENT_PAGE_ID="ec55e31f5861e743bbd21fbff15d80f6"

Now create the database with the exact schema OwnDocs expects:

Create the database
Bash
curl -X POST https://api.notion.com/v1/databases \
  -H "Authorization: Bearer $NOTION_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Notion-Version: 2026-03-11" \
  -d '{
    "parent": { "type": "page_id", "page_id": "'"$PARENT_PAGE_ID"'" },
    "title": [{ "type": "text", "text": { "content": "Docs Feedback" } }],
    "initial_data_source": {
      "properties": {
        "Path": { "title": {} },
        "Helpful": { "checkbox": {} },
        "Submitted": { "date": {} },
        "Comment": { "rich_text": {} }
      }
    }
  }'

The id in the response is your NOTION_DATABASE_ID:

JSON
JSON
{ "object": "database", "id": "218fb892-6f02-e83e-52a5-42a8869bb279" }

NOTE Skip the sharing step and Notion answers with a validation_error about parent.page_id. Bots can't create anything at the workspace root. Share the page, run the same command again, and it goes through.

Option 3: Let the script do it (optional)

OwnDocs ships a small Node script that talks to the Notion API for you. It is optional; Options 1 and 2 already cover everything. Reach for it when you would rather not hand-build the request, or when you want to check an existing database instead of creating a new one.

It reads NOTION_API_KEY and NOTION_DATABASE_ID from your environment or .env.local. It never stores a token and never prints one.

Check that a database you already have carries every field OwnDocs writes:

Terminal
Bash
npm run notion:verify

Add any field that is missing, leaving everything else untouched:

Terminal
Bash
npm run notion:fix

Create a new database from scratch. It asks for the name, lists the pages your integration can write to, and confirms before it writes anything:

Terminal
Bash
npm run notion:create

When it finishes it prints the NOTION_DATABASE_ID line to paste into .env.local.

NOTE notion:fix only adds fields that are absent. If a field exists with the wrong type, the script reports it and stops rather than rewriting your data. Rename or delete that field in Notion, then run it again.

Extra flags:

  • npm run notion:verify -- --json prints a machine-readable result, so CI can gate on the schema.
  • npm run notion:verify -- --database <id> checks a database other than the one in your environment.

Wire it into OwnDocs

For local development, put both values in .env.local:

.env.local
Bash
NOTION_API_KEY="ntn_your-token"
NOTION_DATABASE_ID="218fb8926f02e83e52a542a8869bb279"

For Vercel, add the same two under Project → Settings → Environment Variables, then redeploy.

Result

Open any page and vote. A row lands in the database within a second or two, carrying the page path, the thumbs up or down, the timestamp, and the comment when the reader left one.

Was this page helpful?

© 2026 DevOps. All rights reserved.