> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hiveku.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect Notion

> Sync content between Hiveku and your Notion workspace

Many teams draft blog posts, case studies, and internal docs in Notion — it's great for collaborative writing, but it's not a public website. This integration lets you write in Notion and auto-publish to your Hiveku site, so you never have to copy-paste content again.

<Info>
  This works in both directions. You can pull from Notion into Hiveku (publish a blog), or push from Hiveku into Notion (log form submissions as database rows).
</Info>

## Step 1: Create a Notion Integration

<Steps>
  <Step title="Go to Notion integrations">
    Open [notion.so/my-integrations](https://www.notion.so/my-integrations) and click **New integration**.
  </Step>

  <Step title="Name and configure it">
    * **Name:** `Hiveku Publisher` (or whatever makes sense)
    * **Associated workspace:** the workspace with the content you want to sync
    * **Capabilities:** at minimum check **Read content**. Add **Update content** if you also want to write back to Notion (e.g., mark a page as "Live" after publishing).
  </Step>

  <Step title="Copy the Internal Integration Token">
    Token looks like `secret_abc123...`. Treat it like a password — don't commit it to git.
  </Step>

  <Step title="Share your database with the integration">
    Open the Notion database you want Hiveku to read. Click the **...** menu at the top right, go to **Connections**, and add `Hiveku Publisher`.

    <Warning>
      This step is easy to forget and is the most common cause of `404` errors later. The integration can only see databases that have been explicitly shared with it.
    </Warning>
  </Step>
</Steps>

## Step 2: Find Your Database ID

Open the Notion database as a full page (not embedded). The URL looks like:

```
https://www.notion.so/yourworkspace/abc123def456.../...?v=...
```

The 32-character string between the slash and the `?` is your database ID. Copy it.

## Step 3: Store Credentials in Hiveku

<Steps>
  <Step title="Open your project's env vars">
    Go to **Settings > Environment Variables** in your Hiveku project.
  </Step>

  <Step title="Add two variables">
    * `NOTION_API_KEY` — the integration token from Step 1
    * `NOTION_DATABASE_ID` — the database ID from Step 2
  </Step>

  <Step title="Save and deploy">
    Env vars take effect on the next deploy. Click **Deploy**.
  </Step>
</Steps>

## Step 4: Build the Auto-Publish Workflow

This workflow polls Notion hourly and publishes any page marked `Status = Published`.

<Steps>
  <Step title="Create a new workflow">
    Name it `Notion Publisher`.
  </Step>

  <Step title="Add a Schedule trigger">
    Every hour (or every 15 minutes for faster turnaround).
  </Step>

  <Step title="Query Notion for published pages">
    Add an **HTTP Request** action:

    ```
    POST https://api.notion.com/v1/databases/{{env.NOTION_DATABASE_ID}}/query

    Headers:
      Authorization: Bearer {{env.NOTION_API_KEY}}
      Notion-Version: 2022-06-28
      Content-Type: application/json

    Body:
    {
      "filter": {
        "property": "Status",
        "select": { "equals": "Published" }
      }
    }
    ```
  </Step>

  <Step title="Fetch block content for each page">
    For each result, fetch the page's blocks:

    ```
    GET https://api.notion.com/v1/blocks/{{page.id}}/children
    ```
  </Step>

  <Step title="Convert blocks to Markdown">
    Notion blocks are a JSON tree. Convert to Markdown using a library like `notion-to-md`, or write inline transformation for simple cases (headings, paragraphs, lists, code).
  </Step>

  <Step title="Create or update the blog post">
    Insert into your `blog_posts` table (or whatever your schema uses), keyed by the Notion page ID so re-publishes update rather than duplicate.
  </Step>

  <Step title="Mark the Notion page as Live">
    Update the page's Status property to `Live` so it doesn't re-publish on the next run:

    ```
    PATCH https://api.notion.com/v1/pages/{{page.id}}
    Body: { "properties": { "Status": { "select": { "name": "Live" } } } }
    ```
  </Step>
</Steps>

## Reverse Direction: Hiveku to Notion

Useful for logging: push form submissions, CRM updates, or workflow events into a Notion database as new rows.

```
POST https://api.notion.com/v1/pages

Headers:
  Authorization: Bearer {{env.NOTION_API_KEY}}
  Notion-Version: 2022-06-28

Body:
{
  "parent": { "database_id": "{{env.NOTION_LOGS_DATABASE_ID}}" },
  "properties": {
    "Name": { "title": [{ "text": { "content": "{{trigger.name}}" }}] },
    "Email": { "email": "{{trigger.email}}" },
    "Submitted": { "date": { "start": "{{now}}" }}
  }
}
```

## Handling Images

Notion image URLs expire after 1 hour. If you link directly, your blog images will break the next day.

<Steps>
  <Step title="Download each image">
    When converting blocks, detect image blocks and fetch the image binary before the URL expires.
  </Step>

  <Step title="Re-upload to your assets storage">
    Push to your Hiveku media bucket, Cloudinary, S3, or wherever you host site assets.
  </Step>

  <Step title="Replace the URL in your Markdown">
    Use the permanent URL in your published post, not the Notion-hosted one.
  </Step>
</Steps>

<Warning>
  Notion's API has strict rate limits (3 requests/sec average). Batch where possible, and add a small delay (350ms) between requests in high-volume workflows. Hitting the limit returns `429 Too Many Requests`.
</Warning>

## Verify It Worked

<Steps>
  <Step title="Create a test page in Notion">
    Add a new row to your synced database with Status = Published. Give it a title, some paragraph content, and save.
  </Step>

  <Step title="Trigger the workflow">
    Either wait for the next hourly run, or hit **Run Now** on the workflow page.
  </Step>

  <Step title="Check your site">
    Visit your blog listing page. The new post should appear.
  </Step>

  <Step title="Confirm Notion was updated">
    Back in Notion, the page's Status should now read `Live`.
  </Step>
</Steps>

## Troubleshooting

<AccordionGroup>
  <Accordion title="404 on the database query">
    The integration doesn't have access to the database. Open the database in Notion, click **...** > **Connections**, and add `Hiveku Publisher`. This is the single most common cause of 404s on the Notion API.
  </Accordion>

  <Accordion title="401 Unauthorized">
    Either the `NOTION_API_KEY` env var is wrong, or the integration has been revoked. Go to [notion.so/my-integrations](https://www.notion.so/my-integrations), check the integration exists, and regenerate the secret if needed.
  </Accordion>

  <Accordion title="Images are broken after a day">
    You linked directly to Notion-hosted images instead of re-uploading. Fix the workflow to download and re-upload images to your own asset storage. See the "Handling Images" section above.
  </Accordion>

  <Accordion title="Rich text formatting is lost">
    Notion's block-to-HTML conversion is lossy out of the box. Use a mature library like [`notion-to-md`](https://github.com/souvikinator/notion-to-md) rather than rolling your own parser — it handles callouts, toggles, bookmarks, and nested lists correctly.
  </Accordion>

  <Accordion title="Pages duplicating on every run">
    You're inserting rather than upserting. Use the Notion page ID as a unique key in your blog\_posts table, and either UPDATE on conflict or skip if already present.
  </Accordion>
</AccordionGroup>

## What's Next?

<CardGroup cols={2}>
  <Card title="Webhook Patterns" icon="webhook" href="/how-tos/webhook-patterns">
    Handle inbound webhooks from Notion and other tools
  </Card>

  <Card title="Post a Blog" icon="pen-to-square" href="/how-tos/post-a-blog">
    Other ways to publish content on your site
  </Card>
</CardGroup>
