# Sync To Notion Doc

> Sync finalized Markdown documents from PM-OS to the Notion Documentation DB. Detects, classifies, converts, and pushes MD files as properly formatted Notion pages.

- Skill: `talgacapri/sync-to-notion-doc` (Agent Skill)
- Install (CLI): `npx skillmds@latest add talgacapri/sync-to-notion-doc`
- Raw SKILL.md: https://api.skillmd.com/api/skills/talgacapri/sync-to-notion-doc/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: talgacapri (https://skillmd.com/u/talgacapri)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/talgacapri/sync-to-notion-doc

---


## Quick Start

```
/sync-to-notion                                        → Scan notion-docs/ for unsynced files
/sync-to-notion notion-docs/my-doc.md                  → Sync a specific file from the staging folder
/sync-to-notion outputs/launches/alpha-plan.md         → Sync a file directly from outputs
```

**What you get:** A Notion page in the Documentation DB with the correct Document Type, formatted content, and a sync record in the local file's YAML frontmatter.

**Sync target:** Notion Documentation DB at `https://www.notion.so/Docs-2176409321d9814b8107ea8f9943e76f`
- Data source ID: `21764093-21d9-81a8-baca-000b9e5d9228`

**Time:** 1-2 minutes per document.

---

# /sync-to-notion - Notion Document Sync

## Purpose

Push finalized Markdown documents from PM-OS to the Notion Documentation DB as properly formatted pages. Supports both new page creation and updates to previously synced pages.

## Invocation Patterns

This skill supports three invocation styles:

1. **Explicit file path:** `/sync-to-notion path/to/file.md` syncs a single specific file.
2. **Folder scan:** `/sync-to-notion-doc` (no arguments) scans `notion-docs/` for any MD files that have not been synced yet (no `notion_page_id` in their YAML frontmatter).
3. **Post-workflow prompt:** After any PM-OS skill creates a file in `outputs/`, offer: "Want me to push this to Notion?"

---

## Workflow

### Step 1: Detect and Read the MD File

**If a file path is provided:**
- Read the file at the given path.
- If it does not exist, tell the user and stop.

**If no file path is provided (folder scan):**
- List all `.md` files in `notion-docs/` (excluding `README.md`).
- For each file, check the YAML frontmatter for a `notion_page_id` field.
- Files without `notion_page_id` are unsynced. Present the list and ask the user which ones to sync (or sync all).
- If no unsynced files exist, tell the user and stop.

**Check for existing sync record:**
- If the file already has `notion_page_id` in its YAML frontmatter, this is an update, not a create. Note the `notion_page_id` for Step 5.

---

### Step 2: Classify the Document Type

Map the file to one of the Notion Documentation DB's `Document Type` values by analyzing the file's source path and content.

**Classification rules (in priority order):**

| Signal | Document Type |
|--------|---------------|
| Path contains `prds/` or content has PRD structure (hypothesis, success metrics, rollout) | PRD |
| Path contains `launches/` or content has launch/testing/checklist structure | Specification |
| Path contains `research-synthesis/` or `research/` or content is user research | User Research |
| Path contains `decisions/` or content has decision log structure | Discovery |
| Path contains `strategy/` or content is strategy, OKR, roadmap | Strategy |
| Content describes a process, SOP, or operational procedure | Standard Operating Procedure (SOP) |
| Content is a policy, privacy doc, compliance, or regulatory | Policy |
| Content is a technical standard, API spec, or engineering standard | Standard |
| None of the above | Ask the user to choose |

**Always confirm the classification with the user before proceeding.** Present it as:

> I've classified this as **[Document Type]**. Does that look right, or would you prefer a different type?
>
> Options: Specification, Discovery, PRD, Policy, Standard, Standard Operating Procedure (SOP), Strategy, User Research

---

### Step 3: Set Document Status

Default to **"Draft"** unless the user specifies otherwise.

Ask: "Document status? Default is Draft."

Valid values: Draft, Dev Review, Approved for Dev, In Dev, In Production, Archived

---

### Step 4: Convert MD to Notion-Flavored Markdown

Before converting, fetch the Enhanced Markdown Specification to get the exact syntax:

```
FetchMcpResource: server=user-Notion, uri=notion://docs/enhanced-markdown-spec
```

**Conversion rules:**

1. **Strip YAML frontmatter.** Remove any existing `---` delimited YAML block from the top of the file. The frontmatter is metadata for PM-OS, not Notion content.

2. **Extract the document title.** Use the first `# Heading` in the document as the `Name` property. Remove it from the body content (Notion shows the title separately above the page content).

3. **Convert standard Markdown tables to Notion table format.** Standard Markdown tables (`| col1 | col2 |`) must be converted to Notion's XML table format:
   ```
   <table header-row="true">
   	<tr>
   		<td>Header 1</td>
   		<td>Header 2</td>
   	</tr>
   	<tr>
   		<td>Cell 1</td>
   		<td>Cell 2</td>
   	</tr>
   </table>
   ```

4. **Preserve code blocks as-is.** Notion supports standard fenced code blocks. Do NOT escape special characters inside code blocks.

5. **Convert horizontal rules.** Standard `---` becomes a Notion divider (same syntax, but make sure it is on its own line with blank lines around it).

6. **Preserve headings, lists, bold, italic, strikethrough, links, and inline code.** These are compatible between standard Markdown and Notion-flavored Markdown.

7. **Convert checkboxes.** `- [ ]` and `- [x]` are natively supported.

8. **Strip any HTML that Notion does not support.** If the source MD contains raw HTML (e.g., `<br>`, `<div>`), convert `<br>` to Notion's `<br>` (same), and strip unsupported tags.

9. **Do NOT include the page title in the body content.** The title goes into the `Name` property only.

---

### Step 5: Create or Update the Page in Notion

**For new pages (no existing `notion_page_id`):**

Use `CallMcpTool` with `notion-create-pages`:

```json
{
  "server": "user-Notion",
  "toolName": "notion-create-pages",
  "arguments": {
    "parent": {
      "data_source_id": "21764093-21d9-81a8-baca-000b9e5d9228"
    },
    "pages": [
      {
        "properties": {
          "Name": "<document title>",
          "Document Type": "<classified type>",
          "Document Status": "<status, default Draft>"
        },
        "content": "<converted Notion-flavored markdown body>"
      }
    ]
  }
}
```

**For updates (existing `notion_page_id` in frontmatter):**

Use `CallMcpTool` with `notion-update-page`:

```json
{
  "server": "user-Notion",
  "toolName": "notion-update-page",
  "arguments": {
    "page_id": "<notion_page_id from frontmatter>",
    "command": "replace_content",
    "new_str": "<converted Notion-flavored markdown body>"
  }
}
```

Then update properties if they changed:

```json
{
  "server": "user-Notion",
  "toolName": "notion-update-page",
  "arguments": {
    "page_id": "<notion_page_id from frontmatter>",
    "command": "update_properties",
    "properties": {
      "Name": "<document title>",
      "Document Type": "<classified type>",
      "Document Status": "<status>"
    }
  }
}
```

---

### Step 6: Record the Sync

After a successful push, update the local MD file's YAML frontmatter.

**If the file has no frontmatter yet, add one at the top:**

```yaml
---
notion_page_id: "<page ID returned by Notion>"
notion_url: "<Notion page URL>"
document_type: "<classified type>"
synced_at: "<ISO 8601 timestamp>"
---
```

**If the file already has frontmatter, update these fields:**

- `notion_page_id` - keep the same (or set for first sync)
- `notion_url` - keep the same (or set for first sync)
- `document_type` - update if changed
- `synced_at` - update to current timestamp

This frontmatter is what enables idempotent syncs. On the next `/sync-to-notion-doc` run, the skill detects the `notion_page_id` and updates instead of creating a duplicate.

---

### Step 7: Confirm to User

After a successful sync, report:

> Synced to Notion.
> - **Page:** [Document Title](notion_url)
> - **Type:** Document Type
> - **Status:** Document Status
> - **Action:** Created new page / Updated existing page

If multiple files were synced (folder scan), provide a summary table.

---

## Error Handling

| Error | Action |
|-------|--------|
| File not found | Tell the user the path does not exist |
| Notion MCP not connected | Tell the user to run `/connect-mcps connect to notion` |
| Notion API error on create | Show the error, suggest checking the Notion workspace permissions |
| Notion API error on update (page not found) | The page may have been deleted in Notion. Ask the user if they want to create a new page instead. Remove the stale `notion_page_id` from frontmatter. |
| File has no content (empty) | Tell the user the file is empty and skip |

---

## Duplicate Prevention

The skill uses `notion_page_id` in the local file's YAML frontmatter as the single source of truth for sync state.

- **No `notion_page_id`** = new page, use `notion-create-pages`
- **Has `notion_page_id`** = existing page, use `notion-update-page`

Before creating a new page, optionally search Notion for a page with the same title to warn about potential duplicates:

```json
{
  "server": "user-Notion",
  "toolName": "notion-search",
  "arguments": {
    "query": "<document title>",
    "data_source_url": "collection://21764093-21d9-81a8-baca-000b9e5d9228"
  }
}
```

If a match is found, ask: "A page with this title already exists in Notion. Do you want to update it or create a new one?"

---

## Notion Documentation DB Schema Reference

The target database uses this schema:

| Property | Type | Values |
|----------|------|--------|
| Name | title | Document name (from first H1) |
| Document Type | select | Specification, Discovery, PRD, Policy, Standard, Standard Operating Procedure (SOP), Strategy, User Research |
| Document Status | select | Draft, Dev Review, Approved for Dev, In Dev, In Production, Archived |
| Product Lead | person | (optional, set manually in Notion) |
| Engineering Lead | person | (optional, set manually in Notion) |
| UX Lead | person | (optional, set manually in Notion) |
| GTM Lead | person | (optional, set manually in Notion) |
| Owner | person | (optional, set manually in Notion) |
| Author | person | (optional, set manually in Notion) |
| Document Version (1) | text | (optional, can be set if version info is in frontmatter) |

Person fields require Notion user IDs and cannot be set from Markdown content. They should be assigned manually in Notion after sync.

---

## Integration with Other Skills

This skill works with every skill that creates files in `outputs/`:

- `/prd-draft` creates files in `outputs/prds/` -> classify as PRD
- `/launch-checklist` creates files in `outputs/launches/` -> classify as Specification
- `/user-research-synthesis` creates files in `outputs/research-synthesis/` -> classify as User Research
- `/decision-doc` creates files in `outputs/decisions/` -> classify as Discovery
- `/write-prod-strategy` creates files in `outputs/roadmaps/` or strategy docs -> classify as Strategy
- `/status-update` creates files in `outputs/status-updates/` -> classify as Specification

After any of these skills completes, the assistant can offer: "Want me to push this to Notion?"

The user can either:
1. Say yes to sync immediately from `outputs/`.
2. Move the file to `notion-docs/` first and sync later.

---

## Notion-Flavored Markdown Quick Reference

For the complete spec, always fetch: `notion://docs/enhanced-markdown-spec`

Key differences from standard Markdown:

- **Tables** use XML (`<table>`, `<tr>`, `<td>`) not pipe syntax
- **Toggles** use `<details>` / `<summary>`
- **Callouts** use `<callout icon="emoji">`
- **Colors** use `{color="Color"}` attribute on blocks or `<span color="Color">` for inline
- **Empty lines** need `<empty-block/>` (blank lines are stripped)
- **Indentation** uses tabs, not spaces
- **Page title** goes in properties, not in page content

