← back to Ventura Claw
server/connectors/notion.js
35 lines
// Notion — REAL. Internal integration token (ntn_...) is simplest.
const API = "https://api.notion.com/v1";
const VERSION = "2022-06-28";
function token(c) {
const t = c?.NOTION_TOKEN || process.env.NOTION_TOKEN;
if (!t) throw new Error("NOTION_TOKEN not set");
return t;
}
async function call(method, path, body, c) {
const r = await fetch(`${API}${path}`, {
method,
headers: { "Authorization": `Bearer ${token(c)}`, "Notion-Version": VERSION, "Content-Type": "application/json" },
body: body ? JSON.stringify(body) : undefined
});
const d = await r.json();
if (!r.ok) throw new Error(`notion ${path} ${r.status}: ${d.message || JSON.stringify(d).slice(0,200)}`);
return d;
}
module.exports = {
meta: { id: "notion", name: "Notion", category: "tasks", docsUrl: "https://developers.notion.com/", auth: "oauth2", realImpl: true },
fields: [{ key: "NOTION_TOKEN", label: "Internal Integration Token", type: "password", required: true, hint: "ntn_... — notion.so/my-integrations. Must SHARE each db/page with your integration." }],
configured(c) { return !!(c?.NOTION_TOKEN || process.env.NOTION_TOKEN); },
async health(c) {
if (!this.configured(c)) return { ok: false, reason: "no_token" };
try { const d = await call("GET", "/users/me", null, c); return { ok: true, bot: d.name, type: d.type }; }
catch (e) { return { ok: false, reason: e.message }; }
},
actions: {
async "search"({ query, page_size }, c) { return call("POST", "/search", { query: query||"", page_size: page_size||25 }, c); },
async "databases.query"({ database_id, page_size }, c) { return call("POST", `/databases/${database_id}/query`, { page_size: page_size||25 }, c); },
async "page.create"({ parent, properties, children }, c) { return call("POST", "/pages", { parent, properties, children }, c); },
async "block.append"({ block_id, children }, c) { return call("PATCH", `/blocks/${block_id}/children`, { children }, c); }
}
};