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.
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:
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 The script waits for a terminal status and saves
approval_mode: "exclude".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: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 successfulnotion-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 underrelated_resources; fetched children remain separate files with their own IDs. - Databases —
collection://…data-source URLs from a database fetch are matched tonotion-query-data-sourcesornotion-query-database-viewinputs. 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. - Comments —
notion-get-commentsresponses are attached using the inputpage_id.
unmatched_tool_calls instead of guessing.
View export_notion_sync.py
Full manifest extraction, ID normalization, lineage, and output-generation
example.
Output shape
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.