Skip to main content

Call Notion's reverse-engineered ✦ AI / Custom Agent endpoint from the command line

Project description

notion-agent-cli

把你的 Notion ✦ AI / Custom Agent 变成一条 shell 命令。

A small Python CLI + library that talks directly to Notion's internal POST /api/v3/runInferenceTranscript endpoint using a token_v2 cookie copied from a logged-in browser session. No notion_manager Go proxy, no account pool — just your own session.

The conversations show up in Notion's ✦ AI chat panel under whichever Custom Agent persona you bind (e.g. Jarvis), so you can switch between talking to your agent from Notion's UI and from the shell.

Status

v0.1.0 — first tagged release. The library is live-validated against Notion's real endpoint and adopted by at least one downstream project (NotionAgents uses NotionAgentClient as its inference backend). See docs/STATUS.md for the full live-test ledger and docs/ROADMAP.md for what's still planned.

Install

pipx install notion-agent-cli      # recommended — isolated venv, one-line upgrade
# or
pip install notion-agent-cli       # if you don't use pipx

For the optional FastAPI HTTP wrapper (notion-agent serve):

pipx install 'notion-agent-cli[serve]'

For local development from a clone, pip install -e ".[dev]". The release flow is in docs/RELEASING.md.

Quickstart

# One-shot setup: paste your `token_v2` cookie value from DevTools
# (Application → Cookies → notion.so → token_v2 row → "Value" column).
# The CLI hits /loadUserContent with the token alone, reads your
# user_id back from the response, and picks the (only) workspace.
notion-agent init --token-v2 "v03%3A..."

# Multiple workspaces? Disambiguate with --space-name "TP-Link"
# (case-insensitive) or --space-domain. The first `init` will exit
# with a list of choices and a ready-to-copy rerun line.

# Chat (returns the full reply after streaming completes).
notion-agent chat "用一句话确认你在线。"

Prefer keeping the token out of shell history? Either of these works:

# Token from stdin — never lands in argv / `ps` output:
pbpaste | notion-agent init --token-v2 -

# Or pipe a full document.cookie string (DevTools → Application →
# Cookies → notion.so → right-click → "Copy all as ...") so init
# also pre-fills notion_user_id / notion_browser_id:
pbpaste | notion-agent init --cookie -

For Jarvis-style custom agent binding (so threads surface in the ✦ AI chat panel under your agent name), find your agent's agent_id with notion-agent agents list (the name column shows the page title), then pass it to init:

notion-agent agents list                       # find the agent_id
notion-agent init --token-v2 "v03%3A..." \
                  --agent-name Jarvis \
                  --agent-page-id 2e115375-830d-8184-bf4d-f427d847c6bc

--agent-name alone is not enough — the binding needs --agent-page-id <uuid> (the persistent instructions page id). Passing only the name prints a warning and falls back to the default ✦ AI surface.

CLI surface

notion-agent init     Bootstrap notion_account.json from a token_v2 cookie.
notion-agent chat     Send one prompt, print the reply (text / --json / --stream / --ndjson).
notion-agent doctor   Validate the account file and ping /loadUserContent.
notion-agent models   refresh — pull the current alias→Notion-id map.
notion-agent agents   list    — custom agents in the bound workspace.
notion-agent threads  list    — recent chat threads (filter by --agent).
notion-agent profile  list / current / use <name> / migrate <name>
notion-agent serve    Local FastAPI server (optional [serve] extra).

Run notion-agent <sub> --help for flag details. Every subcommand accepts --account PATH to point at a non-default credential file.

Continuation (multi-turn chats)

chat saves per-thread continuation state under ~/.notionagents/threads/<thread-id>.json after every successful turn, so the next turn can be a follow-up in the same chat-panel thread:

# Turn 1 — capture the thread id.
THREAD=$(notion-agent chat --json "summarize my day so far" | jq -r .thread_id)

# Turn 2 — reuse it.
notion-agent chat --thread-id "$THREAD" "now translate it to chinese"

The thread's model is locked to whatever turn 1 chose — passing --model on a --thread-id continuation is silently ignored, since mid-thread model switches occasionally make Notion reject the partial transcript.

Multi-workspace profiles

Run notion-agent init once per workspace into a named profile file, then swap between them with profile use:

notion-agent init --cookie "..." --account ~/.notionagents/tplink.json
notion-agent init --cookie "..." --account ~/.notionagents/personal.json
notion-agent profile use tplink   # symlinks notion_account.json → tplink.json
notion-agent profile current      # prints "tplink"
notion-agent profile list         # both, * marks active

If you started before profiles existed, profile migrate <name> renames your existing notion_account.json into a named profile and replaces the original with a symlink — non-destructive, no re-init required.

Local HTTP server

notion-agent serve wraps the CLI in a small FastAPI app for orchestrators (n8n, Make, internal Go services) that prefer HTTP over shell-out. Install with the [serve] extra:

pip install -e ".[serve]"
notion-agent serve --host 127.0.0.1 --port 8000

# In another shell:
curl -sN -X POST localhost:8000/chat \
     -H 'content-type: application/json' \
     -d '{"prompt":"hi","stream":true}'   # SSE; data: {"text":"..."} per chunk
curl -s localhost:8000/healthz
curl -s localhost:8000/agents

Endpoints: POST /chat (blocking JSON or SSE), GET /healthz (doctor's checks, 200 / 503), GET /agents, GET /threads. Error codes match the library's ErrorCode taxonomy so callers branch on code without parsing messages.

Calling this from an AI agent

Two paths, pick the one that matches your runtime:

  • Claude Code — the repo ships a skill at .claude/skills/notion-agent/. Open this repo (or a downstream project that depends on the CLI) and the skill auto-loads via its frontmatter trigger phrases ("ask Jarvis", "Notion AI", "via the chat panel").
  • Anything else (Codex, Gemini, openclaw, your own orchestrator) — read docs/AGENTS.md. It has a paste-ready system-prompt snippet at the bottom covering the CLI contract, preflight, error codes, and safety rules in ~40 lines.

Both paths shell out to the same notion-agent binary — make sure it is on $PATH for whatever runtime you use.

Why this exists

Notion's Custom Agents went paid in May 2026 ($10 / 1k credits, 30–60 credits per run, Business+ only). Their built-in ✦ AI chat is free for any Plus seat but only addressable from the UI — not from a cron job, a webhook, or a shell pipe. This CLI bridges that gap by mirroring exactly what the SPA sends, so you get the same free-quota model from bash / python / wherever.

Reverse-engineering notes + the captured chat-panel traffic that this implementation is built against live in docs/01-notion-chat-protocol.md.

Library use

import asyncio
from notion_agent_cli import NotionAgentClient

async def main():
    client = NotionAgentClient("~/.notionagents/notion_account.json")
    try:
        resp = await client.complete(prompt="今天周几?")
        print(resp.text)
        print(f"usage: in={resp.usage.input_tokens} out={resp.usage.output_tokens}")

        # Reuse the thread for a follow-up:
        followup = await client.complete(
            prompt="and yesterday?", thread_id=resp.thread_id,
        )
        print(followup.text)
    finally:
        await client.aclose()

asyncio.run(main())

Async streaming consumers can pass on_text_delta= (sync) or on_text_delta_async= (coroutine, awaited per chunk — used by the FastAPI SSE branch). For raw NDJSON passthrough use client.stream_lines(...).

The public surface kept stable across v0.1: NotionAgentClient, NotionAgentError, ChatResponse, TokenUsage, ErrorCode. Sub-modules (transcript, ndjson, account, models, profile, serve) are importable but their internals can shift between minor releases.

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

notion_agent_cli-0.1.3.tar.gz (100.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

notion_agent_cli-0.1.3-py3-none-any.whl (51.5 kB view details)

Uploaded Python 3

File details

Details for the file notion_agent_cli-0.1.3.tar.gz.

File metadata

  • Download URL: notion_agent_cli-0.1.3.tar.gz
  • Upload date:
  • Size: 100.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for notion_agent_cli-0.1.3.tar.gz
Algorithm Hash digest
SHA256 84a3ddf48e69c64bf6ac0def485f310dccc85c3c615d67c9a56796749c7159d8
MD5 1566fd3763824b5aa684d88c8f05563e
BLAKE2b-256 2e7848e4e8d9cc822bd83be81a74dc43fbfcc7759bc9c022bc26c78237cda865

See more details on using hashes here.

Provenance

The following attestation bundles were made for notion_agent_cli-0.1.3.tar.gz:

Publisher: release.yml on ChenyqThu/notion-agent-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file notion_agent_cli-0.1.3-py3-none-any.whl.

File metadata

File hashes

Hashes for notion_agent_cli-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 f75cd68932ba43751d281f81be09be01f48d57823ad4cdc15290cbe5bc85925f
MD5 67bdfcdaf0049959e153205194d3017b
BLAKE2b-256 1bd56b3179cd842820cec63092e5bd6ff009a1c6210a28bc86484660964905c1

See more details on using hashes here.

Provenance

The following attestation bundles were made for notion_agent_cli-0.1.3-py3-none-any.whl:

Publisher: release.yml on ChenyqThu/notion-agent-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page