Content versions
A content version is a saved snapshot of a page's body content — the same rows that back GET /pages/{pageId}/content (which always serves the latest one) and POST /pages/{pageId}/content (which creates a new one, or collapses into the latest). This page covers listing and reading version history.
All content-version endpoints require sites:read.
Version ids are NOT append-only. Consecutive manual edits (
POST /pages/{pageId}/contentwithout an intervening non-manual change) collapse into the same version row rather than creating a new one — seecollapsedon the content POST response. Treat this list as a snapshot of current history, not a stable ledger: don't cache version ids across a save and assume the set you fetched earlier still lines up one-to-one with what actually happened. If you need to know whether your last save produced a new row, readcollapsedoff that response — don't infer it from a version count.
source values
| Source | Meaning |
|---|---|
manual_edit |
Written by the editor UI or POST /pages/{pageId}/content |
crawl |
Overwritten by a recrawl re-scraping the live page (POST /pages/{pageId}/recrawl) |
outline |
Produced by accepting generated outline sections |
action |
Produced by an AI action being applied |
Other values may appear over time — treat source as an informational string, not a closed enum.
GET /pages/{pageId}/content-versions
List a page's content versions, newest first. Without doc bodies — each item is the lean shape; fetch a specific version's detail endpoint for its rendered markdown.
Scope: sites:read
Path parameters
| Parameter | Type | Description |
|---|---|---|
pageId |
integer | The page's ID |
Query parameters
| Parameter | Type | Default | Notes |
|---|---|---|---|
page |
integer | 1 |
1-indexed. Minimum 1. |
limit |
integer | 20 |
Minimum 1, maximum 100. |
Request
curl "https://new.metamonster.ai/api/v1/pages/5001/content-versions?limit=20" \
-H "Authorization: Bearer mm_YOUR_API_KEY"
Response 200
{
"data": [
{
"id": 9301,
"source": "manual_edit",
"status": "draft",
"word_count": 655,
"created_at": "2026-08-01T10:00:00Z",
"updated_at": "2026-08-01T10:00:00Z"
},
{
"id": 9300,
"source": "crawl",
"status": "draft",
"word_count": 640,
"created_at": "2026-07-20T09:20:00Z",
"updated_at": "2026-07-20T09:20:00Z"
}
],
"meta": { "total": 12, "page": 1, "per_page": 20 }
}
Each item (lean content-version shape):
| Field | Type | Description |
|---|---|---|
id |
integer | Content version ID |
source |
string | Which write path produced this version — see source values |
status |
string | Internal lifecycle status. Currently always draft; reserved for future workflow states. |
word_count |
integer | null | Body word count at this version. Populated for every source (including crawl); null only ever appears on rows written before this field existed. |
created_at |
string | When this version was created |
updated_at |
string | Last update — bumped in place when a later manual edit collapses into this version |
Errors
| Status | When |
|---|---|
404 not_found |
No such page in your organization (Page not found) |
GET /pages/{pageId}/content-versions/{versionId}
Fetch one content version with its body rendered as markdown. The underlying ProseMirror document never leaves the API — you only ever get markdown.
Scope: sites:read
Path parameters
| Parameter | Type | Description |
|---|---|---|
pageId |
integer | The page's ID |
versionId |
integer | The content version's ID |
Request
curl https://new.metamonster.ai/api/v1/pages/5001/content-versions/9300 \
-H "Authorization: Bearer mm_YOUR_API_KEY"
Response 200
{
"data": {
"id": 9300,
"source": "crawl",
"status": "draft",
"word_count": 640,
"created_at": "2026-07-20T09:20:00Z",
"updated_at": "2026-07-20T09:20:00Z",
"body_markdown": "# Pricing\n\nSee our plans below…"
}
}
Same fields as the list shape, plus:
| Field | Type | Description |
|---|---|---|
body_markdown |
string | This version's body content rendered to markdown. Empty string "" if the stored document is malformed or empty. |
Errors
| Status | When |
|---|---|
404 not_found |
No such page in your organization, or no such content version on that page (Page not found / Content version not found) |
Rolling back to a previous version
There's no dedicated rollback endpoint — a rollback is just a normal save whose content happens to be an old version's:
# 1. Fetch the version you want to restore
curl https://new.metamonster.ai/api/v1/pages/5001/content-versions/9300 \
-H "Authorization: Bearer mm_YOUR_API_KEY"
# → { "data": { "body_markdown": "# Pricing\n\n…", … } }
# 2. Check the page's CURRENT version (so you don't clobber a newer edit)
curl https://new.metamonster.ai/api/v1/pages/5001/content \
-H "Authorization: Bearer mm_YOUR_API_KEY"
# → { "data": { "content_version_id": 9302, … } }
# 3. Save the old markdown as a new version, guarded by base_version_id
curl -X POST https://new.metamonster.ai/api/v1/pages/5001/content \
-H "Authorization: Bearer mm_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "markdown": "# Pricing\n\nSee our plans below…", "base_version_id": 9302 }'
This creates a new version (source manual_edit) with the restored content — it doesn't delete or rewrite history, so the version you rolled back from is still there if you change your mind again. Passing base_version_id (the version you read in step 2) makes the restore fail with 409 conflict instead of silently overwriting a page that was edited again between steps 2 and 3.