# Page markdown

Read and write a page as Notion's enhanced markdown, with the async tasks Notion runs for a large create or update.

Page markdown lets an agent read a whole page as one markdown string and edit it by search and replace, instead of walking its blocks. The blocks API reads the same edit, because both views are one page:

```bash
curl -s -X POST "$RYSTIC_URL/_rystic/scenario" -d '{"name":"notion_team_wiki"}'
curl -s "$RYSTIC_URL/v1/pages" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' -H 'Content-Type: application/json' -d '{"parent":{"page_id":"a1000000-0000-4000-8000-000000000101"},"markdown":"# Release notes\n\nDraft one\n\n- [ ] Tag it"}' | jq -c '{id}'
# -> {"id":"73d01b77-cb6c-4662-8000-000000000001"}
curl -s -X PATCH "$RYSTIC_URL/v1/pages/73d01b77-cb6c-4662-8000-000000000001/markdown" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' -H 'Content-Type: application/json' -d '{"type":"update_content","update_content":{"content_updates":[{"old_str":"Draft one","new_str":"Final copy"}]}}' | jq -c '{markdown}'
# -> {"markdown":"Final copy\n- [ ] Tag it"}
curl -s -X PATCH "$RYSTIC_URL/v1/pages/73d01b77-cb6c-4662-8000-000000000001/markdown" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' -H 'Content-Type: application/json' -d '{"type":"update_content","update_content":{"content_updates":[{"old_str":"no such text","new_str":"x"}]}}' | jq -c '{status, code}'
# -> {"status":400,"code":"validation_error"}
curl -s "$RYSTIC_URL/v1/blocks/73d01b77-cb6c-4662-8000-000000000001/children" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' | jq -c '.results | map(.type)'
# -> ["paragraph","to_do"]
```

### Reading

`GET /v1/pages/{page_id}/markdown` renders a page's blocks as Notion's enhanced markdown. A block with no markdown form renders as `<unknown>`, and its id is listed in `unknown_block_ids`.

### Writing

`PATCH /v1/pages/{page_id}/markdown` takes one command and returns the page's new markdown:

| Command | What it does |
|---|---|
| `update_content` | searches and replaces |
| `replace_content` | rewrites the whole page |
| `insert_content` or `replace_content_range` | the older commands, which edit part of the page |

A selection that matches nothing returns 400 `validation_error`.

`POST /v1/pages` with `markdown` creates the blocks the markdown describes. A leading `# ` line is lifted off as the page's title and never becomes a block.

### Async tasks

Creating a page with `markdown`, or updating a page's markdown, with `allow_async: true` returns 202 with a `queued` task and `poll_after_seconds: 2`. `GET /v1/async_tasks/{task_id}` reads it as `succeeded`, with the result, on the first poll, as Notion's does:

```bash
curl -s "$RYSTIC_URL/v1/pages" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' -H 'Content-Type: application/json' -d '{"parent":{"page_id":"a1000000-0000-4000-8000-000000000101"},"markdown":"# Async page","allow_async":true}' | jq -c '{object, status, poll_after_seconds}'
# -> {"object":"async_task","status":"queued","poll_after_seconds":2}
curl -s "$RYSTIC_URL/v1/async_tasks/task_ffab1951720d4c8c8000000000000001" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' | jq -c '{object, status}'
# -> {"object":"async_task","status":"succeeded"}
```

---

Source: https://www.rystic.ai/docs/simulators/notion/features/page-markdown · Markdown: https://www.rystic.ai/docs/simulators/notion/features/page-markdown.md
