notion-content-pipeline
Push local .md files to Notion and pull edits back. Track a content pipeline DB with
statuses (Seed → Draft → Review → Published).
Scripts
scripts/notion_content_sync.py— push/pull individual files or all at oncescripts/create_pipeline_db.py— create a Notion database for content pipeline trackingscripts/pipeline_advance.py— full round-trip advance: pull → humanize → fact-check → push → status update
Configuration
NOTION_API_KEY=secret_... # Notion integration token
NOTION_PARENT_PAGE_ID=<uuid> # Parent page ID for sandbox + pipeline DB
NOTION_SYNC_MAP=~/.notion_sync_map.json # Where to store file ↔ page ID mapping
CONTENT_DIR=./content # Directory containing .md files
Or pass --sandbox-id and --sync-map as CLI flags.
Quick start
# Push a single file to Notion
python3 scripts/notion_content_sync.py push content/my-post.md
# Push all .md files in content/
python3 scripts/notion_content_sync.py push-all
# Pull Notion edits back to local file
python3 scripts/notion_content_sync.py pull content/my-post.md
# List all tracked pages with Notion URLs
python3 scripts/notion_content_sync.py list
# Create the Content Pipeline database
python3 scripts/create_pipeline_db.py
Sync map
File ↔ Notion page ID mapping stored in JSON. Example:
{
"content/my-post.md": "abc123-...",
"content/other-post.md": "def456-..."
}
Default location: ./notion_sync_map.json (override with NOTION_SYNC_MAP env var).
Overwrite behaviour
On push, if the file is already tracked, the existing Notion page is archived and
a fresh page is created. This avoids block-count drift from repeated pushes.
Use --no-overwrite to skip if already pushed.
Pipeline DB schema
Created by create_pipeline_db.py:
- Title — post title
- Platform — Blog / LinkedIn / Both
- Status — Seed → Draft → Humanized → In Review → Published
- Hook — one-sentence pitch
- Est. Read Time — < 1 min / 2-3 min / 5-7 min / 10+ min
- Published URL — final URL once live
- Source — where the idea came from
pipeline_advance.py — automated round-trip
# Full advance: pull → humanize → fact-check → push → status
python3 scripts/pipeline_advance.py advance content/my-post.md
# Skip steps individually
python3 scripts/pipeline_advance.py advance content/my-post.md --skip-humanize
python3 scripts/pipeline_advance.py advance content/my-post.md --skip-factcheck
# Preview without making changes
python3 scripts/pipeline_advance.py advance content/my-post.md --dry-run
What it does
- Pulls Notion edits back to your local
.mdfile - Humanizes — applies mechanical AI-pattern fixes in Python (em dash → comma,
"utilize" → "use", filler openers, copula avoidance, curly quotes). Flags
rule-of-three and AI-vocab patterns for LLM review. Writes a
.humanizer.diffalongside the file. - Fact-checks — runs
skills/fact-checker/scripts/fact_check.pyif available; otherwise skips gracefully. Report saved as.factcheck.txt. - Pushes the humanized file back to Notion (archives old page, creates fresh one).
- Updates status in the Content Pipeline DB:
Draft→Humanized(after humanize + fact-check)Humanized→In Review(after fact-check only)- Never advances past
In Review— that's Nissan's decision.
Extra env var
NOTION_PIPELINE_DB_ID=312c9a82-0734-81bd-81a6-e58d0365e404 # optional, hardcoded fallback
See also
references/api_notes.md— Notion API quirks and block type reference