The idea that broke my brain a little
What if your automation logic lived in Notion — not as a diagram, not as a config file, but as a plain-English page you just... edit?
No redeploy. No YAML. No drag-and-drop node editors.
Just:
- Open a Notion page
- Write what you want in plain English
- Run
notion-flows run - Claude reads your page and executes it against your Notion databases
That's notion-flows.
What it does
notion-flows is a TypeScript CLI that treats Notion pages as executable business logic.
You have a registry page in Notion. Each child page is a "flow" — a plain-English description of an automation. The CLI discovers those pages, sends them to Claude with Notion MCP tools, and Claude executes the instructions step by step.
Three example flows are included out of the box:
🔥 Follow Up Cold Leads
This flow finds leads who haven't been contacted in 7 or more days
and drafts personalized follow-up messages.
## Conditions
• Check Contacts DB for Status = "Lead" or "Prospect"
• Filter where Last Contacted is 7+ days ago
• If Deal Value over $2,000, tag as hot
## Actions
• Draft a warm, professional follow-up for each contact
• Update "Next Action" field with the draft message
• Update "Last Contacted" to today
📝 Content Idea Generator
Generate 5 high-value content ideas for a TypeScript consulting business
targeting early-stage startups, add them to the Content Pipeline database,
and write a full first draft for the highest ROI idea.
📊 Weekly CEO Brief
Generate a weekly executive brief by reading all databases,
then append it to this page.
These aren't config files. They're just text. You can rewrite them any time and the behavior changes on the next run.
The agentic loop
The core of notion-flows is a Claude agentic loop with Notion MCP tools.
while (toolCallCount < 30) {
const response = await claude.messages.create({
model: 'claude-opus-4-5',
max_tokens: 4096,
system: systemPrompt,
tools: notion.getMCPTools(), // query, create, update, append, get
messages
})
if (response.stop_reason === 'end_turn') {
finalSummary = extractText(response)
break
}
if (response.stop_reason === 'tool_use') {
messages.push({ role: 'assistant', content: response.content })
const toolResults = []
for (const block of response.content) {
if (block.type === 'tool_use') {
const result = await notion.executeTool(block.name, block.input)
toolResults.push({
type: 'tool_result',
tool_use_id: block.id,
content: JSON.stringify(result)
})
}
}
messages.push({ role: 'user', content: toolResults })
}
}
Claude gets 5 Notion MCP tools:
-
notion_query_database— filter & sort any database -
notion_create_page— create DB entries or sub-pages -
notion_update_page— patch any page properties -
notion_append_blocks— write rich content to a page -
notion_get_page— retrieve page properties
After each run, the flow's own Notion page gets a run log appended automatically — timestamp, summary, and every action taken.
Terminal output
⚡ notion-flows v1.0.0
⚡ Executing: 🔥 Follow Up Cold Leads
Source: https://notion.so/xxxxx
────────────────────────────────────────────────────────────
💭 I'll query the Contacts database for leads not contacted recently...
🔧 notion_query_database (1/30) ✓
💭 Found 3 contacts — drafting follow-ups and tagging one as hot...
🔧 notion_update_page (2/30) ✓
🔧 notion_update_page (3/30) ✓
🔧 notion_update_page (4/30) ✓
🔧 notion_append_blocks (5/30) ✓
✅ Updated 3 contacts with follow-up drafts. Tagged 1 as hot ($4,200 deal).
────────────────────────────────────────────────────────────
📊 Run Summary
────────────────────────────────────────────────────────────
✓ Follow Up Cold Leads — 3 records, 8.4s
Watch mode
npx tsx src/cli.ts watch
Polls every 60 seconds. If you edit a flow page in Notion, it automatically re-runs that flow. Your Notion page is your CI pipeline.
Project structure
notion-flows/
├── src/
│ ├── cli.ts # setup / list / run / watch
│ ├── types/index.ts # Flow, FlowRunResult, NotionWrite
│ ├── runtime/notion.ts # Notion client + MCP tools
│ ├── executor/flow-executor.ts # Agentic Claude loop
│ └── utils/setup.ts # One-command workspace scaffolder
├── package.json
├── tsconfig.json
└── .env.example
Tech stack:
- TypeScript (ESM, strict)
-
@anthropic-ai/sdk— Claudeclaude-opus-4-5 -
@notionhq/client— Notion REST API -
chalk+ora— terminal UX -
dotenv+zod
Get started in 3 commands
git clone https://github.com/dinesh0666/notion-flows
cd notion-flows
npm install
cp .env.example .env
# Add NOTION_API_KEY and ANTHROPIC_API_KEY to .env
npx tsx src/cli.ts setup <your-notion-page-id>
# Prints DB IDs → paste them into .env
npx tsx src/cli.ts run
The setup command scaffolds your entire workspace:
- Creates the ⚡ notion-flows registry page
- Creates Contacts, Content Pipeline, and Revenue databases with full property schemas
- Seeds all 3 example flow pages
Why this matters
Every automation tool eventually forces you to learn its interface — a canvas, a config syntax, a DSL. notion-flows flips that: the interface is a Notion page, which you already know.
You can share flows with non-technical teammates. They can edit the logic. You re-run the CLI. No PRs, no deployments.
The Notion page IS the code.
What's next
-
Scheduled triggers — the
trigger: schedule: 0 9 * * 1syntax is already parsed, just needs a cron runner wired up - Multi-workspace support — share flows between orgs
- Flow versioning — Notion's page history is already your git log
Built for the Notion dev.to challenge. Source code on GitHub.

Top comments (0)