We build custom applications 5x faster and cheaper 🚀
Book a Free Consultation
Stuck on an error? Book a 30-minute call with an engineer and get a direct fix + next steps. No pressure, no commitment.
To integrate Notion with OpenClaw, expose a small, explicit skill that makes authenticated REST calls to Notion’s public API (using either a Notion integration token or an OAuth flow), store the token(s) securely in ClawHub configuration, and keep any webhooks, schedulers, or persistent state outside the agent runtime (a simple HTTPS endpoint and a durable store or queue). Configure the skill in ClawHub with the required environment variables and scopes, implement and test basic Notion calls (read/search, create/update pages or database rows) using the Notion REST API, and debug by inspecting API responses, logs, and permission/sharing settings in Notion.
// <b>//</b> Node.js example using fetch (works in modern Node versions)
const fetch = require('node-fetch');
async function createNotionPage(databaseId, title, notionToken) {
const url = 'https://api.notion.com/v1/pages';
const body = {
parent: { database_id: databaseId },
properties: {
Name: {
title: [
{ text: { content: title } }
]
}
// <b>//</b> Add other properties as required by your database schema
}
};
const res = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${notionToken}`,
'Notion-Version': '2022-06-28',
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!res.ok) {
const text = await res.text();
throw new Error(`Notion API error ${res.status}: ${text}`);
}
return res.json();
}
// <b>//</b> Example usage:
// createNotionPage(process.env.NOTION_DATABASE_ID, 'My page title', process.env.NOTION_TOKEN)
// .then(r => console.log('created', r))
// .catch(err => console.error(err));
# <b>//</b> curl example
curl -X POST "https://api.notion.com/v1/pages" \
-H "Authorization: Bearer $NOTION_TOKEN" \
-H "Notion-Version: 2022-06-28" \
-H "Content-Type: application/json" \
-d '{
"parent": { "database_id": "'"$NOTION_DATABASE_ID"'" },
"properties": {
"Name": { "title": [{ "text": { "content": "Hello from OpenClaw" } }] }
}
}'
Speak one‑on‑one with a senior engineer about your no‑code app, migration goals, and budget. In just half an hour you’ll leave with clear, actionable next steps—no strings attached.
1
Set your Notion integration token and Notion-Version as environment variables for the OpenClaw skill (for example NOTION_API_TOKEN and NOTION_VERSION), configure them in ClawHub/agent runtime, and have the skill read those vars and send them as HTTP headers: Authorization: Bearer <token> and Notion-Version: <version>. Ensure the runtime only exposes the token to the skill that needs it.
Set env vars in your connector config; skill code reads them and uses standard Notion headers.
2
Use the Notion Blocks API pagination (results, has_more, next_cursor) and recursively fetch blocks with has_children. In an OpenClaw skill, authenticate with an env var, iterate pages until has_more is false, and fetch nested children on demand—push long-running/stateful work outside the agent runtime.
// realistic Node.js using global fetch (Node18+)
const NOTION = 'https://api.notion.com/v1';
const KEY = process.env.NOTION_API_KEY;
async function fetchChildren(blockId){
let cursor = undefined;
const all = [];
do{
const qs = new URLSearchParams();
qs.set('page_size','100');
if(cursor) qs.set('start_cursor', cursor);
const res = await fetch(`${NOTION}/blocks/${blockId}/children?${qs}`,{
headers:{ 'Authorization':`Bearer ${KEY}`, 'Notion-Version':'2022-06-28' }
});
const js = await res.json();
all.push(...js.results);
cursor = js.has_more ? js.next_cursor : undefined;
} while(cursor);
// fetch nested children
for(const b of all){
if(b.has_children){
b.children = await fetchChildren(b.id);
}
}
return all;
}
3
Direct answer: Extract file/embed URLs from a Notion CLI export or via the Notion SDK, download each URL (curl or node-fetch), then POST the file to your OpenClaw asset-store REST upload endpoint using an API key stored in an environment variable; keep state and retries outside the agent runtime.
const fetch = require('node-fetch');
const FormData = require('form-data');
const UPLOAD_URL = process.env.OPENCLAW_ASSET_UPLOAD_URL;
const KEY = process.env.OPENCLAW_ASSET_KEY;
async function mirror(url, filename){
<b>//</b> download file
const r = await fetch(url);
const stream = r.body;
<b>//</b> upload to asset-store
const form = new FormData();
form.append('file', stream, { filename });
const res = await fetch(UPLOAD_URL, {
method:'POST',
headers: { Authorization: `Bearer ${KEY}`, ...form.getHeaders() },
body: form
});
return res.ok? await res.json(): { error: await res.text() };
}
4
Implement incremental sync by persisting a cursor (e.g., Notion's last_edited_time) outside the agent (ClawHub secrets or a small DB), fetch only items newer than the cursor, deduplicate by tracking Notion id + content hash and upserting, and handle Notion rate limits by honoring 429/Retry-After with exponential backoff and retry. Only advance the cursor after successful processing and durable storage of dedupe keys.
// Node.js example
async function callNotion(url, opts){
for(let i=0;i<6;i++){
const res=await fetch(url,opts);
if(res.status===429){
const ra=Number(res.headers.get('Retry-After')||1);
await new Promise(r=>setTimeout(r,(2**i)*1000*ra));
continue;
}
return res;
}
throw new Error('rate limited');
}
This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.
From startups to enterprises and everything in between, see for yourself our incredible impact.
Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.Â