Skip to main content
This recipe uses a background agent task to discover and read Notion content, then turns the task’s persisted tool inputs and outputs into clean JSON for a search index, RAG pipeline, or local knowledge base. The agent decides what to discover and fetch. After it finishes, your application uses the task’s session_id to download the complete tool trace and groups those responses by stable Notion page ID.

Full cookbook source

View the complete scripts, tests, and usage guide in the public caylex-ai/caylex-cookbooks repository.

How it works

The final manifest tells the exporter which resources the agent intentionally selected. The trace supplies the actual page bodies, database rows, and comments returned by Notion.

Endpoints used

Before you start

You need:
  • a server-side platform access token;
  • the navigator API key (ck_…) for the project whose Notion connection the agent should use;
  • the connected user’s email; and
  • optionally, a project or global skill that instructs the agent how to discover, fetch, and report resources.
The trace can contain complete customer documents and database rows. Keep the platform token and generated files server-side, store exports securely, and do not commit task output or raw traces to source control.

Manifest contract

The exporter looks for the first JSON object beginning with {"schema_version":"1.0" in the task report. This remains reliable even if the agent accidentally writes a short explanation before its JSON. Your prompt or skill should require this minimum shape:
Only include a resource after the agent successfully calls notion-fetch. Search results alone do not contain the substantive file content.

The procedure

1

Configure credentials

Keep all credentials in environment variables:
2

Launch and capture the task

Run the task lifecycle script with a prompt and optional skill reference. Read-only sync tasks should use approval_mode: "exclude".
The script waits for a terminal status and saves submission.json, task-status.json, and raw-trace.json.
3

Build the resource-centric export

Pass the captured task response and trace into the exporter:
4

Load files into your index

Iterate through files. Use each stable id as the source key, content as the primary page body, and preserve database queries and comments as related content with their provenance.

What the runner does

The lifecycle runner submits a request shaped like:
It then polls GET /agent-task/{task_id} until COMPLETED, INCOMPLETE, or FAILED. Once session_id is available, it downloads every trace page with limit=100.

View run_background_file_sync.py

Full submission, polling, pagination, timeout, and error-handling example.

How tool responses are grouped

Every manifest resource is matched to a successful notion-fetch input using a normalized Notion UUID. This deduplicates repeated fetch attempts while retaining the contributing tool-call IDs. Some nested resources require an additional relationship:
  • Child pages<page url="…"> references are recorded under related_resources; fetched children remain separate files with their own IDs.
  • Databasescollection://… data-source URLs from a database fetch are matched to notion-query-data-sources or notion-query-database-view inputs. The query response is attached to the canonical database file.
  • Rows — page URLs returned by database queries are preserved in row_links. If the agent fetched a row page, it also appears as its own file.
  • Commentsnotion-get-comments responses are attached using the input page_id.
If a relationship is missing or ambiguous, the exporter records it under unmatched_tool_calls instead of guessing.

View export_notion_sync.py

Full manifest extraction, ID normalization, lineage, and output-generation example.

Output shape

Database responses remain structured under database_queries rather than being concatenated into the page body. This makes it possible to update, re-embed, or audit page content and database rows independently.

Scope and extensions

The cookbook covers pages, child pages, databases/data sources, rows, and comments. Meeting transcripts and file attachments need additional collection logic:
  • fetch meeting-note pages with include_transcript: true; and
  • download signed attachment URLs promptly, then apply the appropriate text, PDF, image, or OCR parser.
Add those only when your indexing use case needs them because they can substantially increase trace size and processing cost.