Notion connector
Connect a Notion workspace, review its Git-backed scope, and keep selected pages and databases available to ctx|.
The Notion connector mirrors selected workspace context into a GitHub
repository, then sends that repository through the normal ctx| ingestion
pipeline. The generated notion/config.yaml pull request makes the scope
reviewable before content sync begins.
ctx| reads Notion pages and databases. It does not create or update Notion content.
Fully managed app flow
The hosted ctx| service supplies the Notion OAuth application and webhook
configuration. Self-hosted operators register a private OAuth app in the
connector setup wizard (client ID, secret, then Add webhook). Setting
NOTION_* environment variables is an optional operator shortcut, not a
required restart. See Self-host Notion.
What is mirrored
You can select pages and databases that the ctx| Notion integration can already see. Their related content can include:
- Selected pages as Markdown, including descendant pages added later.
- Selected databases as an index page, a
table.csvsnapshot, and one Markdown file per row (properties plus page body). - Headings, lists, tasks, quotes, code, and in-scope page links rewritten to relative Git paths when the target is also mirrored.
- Notion-hosted and explicitly embedded external images, files, video, audio,
and PDFs copied into an
assets/directory beside the owning page or row.
Markdown uses relative links to copied assets. Expiring Notion download URLs are never written to git. Assets over 25 MiB, pages or rows over their 100 MiB asset budget, and unsafe or failed external downloads keep a Notion page link or caption stub. Page comments are not mirrored.
Page sharing is the access gate
Notion only lists pages and databases already shared with the ctx| integration. Share those resources during the OAuth picker, or later in Notion, then choose Refresh Notion resources in setup. Review the selected scope and the generated pull request before merging it. Repository access becomes the practical access boundary for the mirrored files.
Prerequisites
- Permission to authorize an application in the Notion workspace and share the pages or databases that should be mirrored.
- A ctx| GitHub connection with access to the destination repository.
- Permission to merge the generated configuration pull request.
Configuration source of truth
Notion scope exists only in notion/config.yaml:
- Draft scope is the YAML on the generated configuration pull request's branch.
- Live scope is the YAML on the selected target branch after that pull request is merged.
ctx| does not keep a second draft or live scope outside Git. The connection tracks where content belongs and whether setup is complete; the merged YAML remains the source of truth for what Notion content is approved.
Setup flow
Connect Notion
Open Connectors, choose Add connection, then select Notion. On a self-hosted deployment without shared env credentials, complete Register Notion OAuth app and Add webhook first, then authorize the workspace in the Notion popup and share the pages or databases the connector should be allowed to read.
Confirm GitHub
Notion content is stored in Git before ingestion. If GitHub is not connected for this ctx| organization, complete the GitHub App installation first.
Choose a context repository
Select an existing connected repository. A dedicated private repository usually gives the clearest ownership and access boundary; create it in GitHub first, grant the ctx| GitHub App access, then refresh the repository list in ctx|.
Select scope
Select at least one page or database. Page selection includes that page's child pages, including pages added later. Database selection includes the database rows. Keep the selection narrow when the workspace holds mixed private and shared notes.
Merge the configuration pull request
ctx| opens a pull request containing notion/config.yaml. Review the
selected pages and databases, then merge it.
The pull request branch YAML is the draft. The target branch YAML becomes live only after merge; the setup dialog submits scope to that Git workflow rather than storing another copy.
Wait for initial sync and ingestion
After GitHub reports the merge, ctx| performs the first Notion mirror and starts repository ingestion. The connector reaches Connected when the mirror succeeds. Repository indexing may continue afterwards; see Ingestion for the indexing lifecycle.
Files in the repository
Mirrored files live under notion/:
- Pages:
notion/pages/<slug>--<id>/index.md, with assets under the adjacentassets/directory and child pages nested under their ancestors. - Databases:
notion/databases/<slug>--<id>/index.md,table.csv, androws/<slug>--<id>/index.md.
Paths include stable Notion IDs, so renaming a page or database does not create a second copy.
Do not hand-edit generated content. Change scope through Manage scope and review the replacement configuration pull request.
Keeping content current
Notion sends signed webhook events to ctx| when supported entities change. While the connector is live, ctx| validates the event against the Git scope, updates the affected files, and starts another repository ingestion. Incremental updates re-mirror the affected top-level resource (a page subtree or a database), not a single block. Events that arrive before the connector is live, including during initial sync, are skipped rather than queued for later. Missed webhook events are not automatically backfilled: repair webhook delivery, then update the affected pages or databases in Notion again so fresh events are delivered, or use Retry content sync after a failed initial mirror.
Verify the complete flow
- Complete Notion OAuth and confirm the expected workspace appears.
- Share at least one page or database with the ctx| integration, then select it in setup.
- Open and merge the generated
notion/config.yamlpull request. - Confirm the connector reaches Connected and the repository contains
generated files under
notion/. - Edit a mirrored page in Notion and confirm the matching Git file changes.
- Confirm the destination repository reaches an indexed state in ctx|.
Steps 3 and 4 test the GitHub push webhook and initial mirror. Step 5 separately tests Notion webhook delivery.
Troubleshooting
Authorization fails
Allow popups and retry. On fully managed ctx|, report persistent OAuth errors to support. On a self-hosted deployment, finish Register Notion OAuth app and Add webhook in the wizard (or the optional env shortcut) and confirm the callback URI matches exactly.
No pages or databases appear
The integration can only list resources already shared with it. Share the page or database with the ctx| Notion app in Notion, then choose Refresh Notion resources. Empty search results usually mean a sharing problem, not a missing workspace.
No pull request appears
Confirm that at least one page or database is selected and that the GitHub App has write access to the destination repository. Empty repositories are supported; ctx| initializes the default branch before opening the pull request. If setup reports that pull request creation failed, open the connector and choose Retry configuration pull request.
The connector stays on “Approve configuration”
The configuration pull request must be merged, not merely approved. Confirm
that notion/config.yaml reached the default branch and that GitHub webhooks
for the ctx| GitHub App are healthy.
Content sync failed
Open the connector and choose Retry content sync. This retries the mirror
without opening another configuration pull request. If it fails again, ask the
operator to check worker logs, Notion token refresh, GitHub write access, and
outbound access to api.notion.com.
Changes in Notion do not arrive
Initial sync can still succeed when webhooks are misconfigured. Ask the operator to verify the Notion integration webhook URL and signing secret. See Self-host Notion for the exact settings.
Notion authorization was revoked
Open the connector and authorize Notion again. Incremental updates stop while the workspace authorization is revoked. Pages unshared from the integration disappear from the searchable list; they remain in Git until you remove them from scope and merge the replacement configuration pull request.
Removing the connector
Removing the connector deletes its ctx| authorization and repository binding
from the connection record. It does not delete notion/config.yaml, generated
files, or Git history from the destination repository; remove those in GitHub
if they are no longer required.