Post mutation endpoints
Five endpoints for modifying posts you've already published. None of them change a post's URL — only its content, publication state, visibility, or tags.
All four accept either API-key or session auth and all fall under the general 120 req/min rate bucket.
Updates to existing posts don't consume a document slot, so these endpoints
all work on Free even when the workspace is at 10/10. Only
POST /api/v1/publish enforces the cap.
:postId can be either the post UUID or the post slug. Slug lookups support
?workspaceId=ws_123 for disambiguation; the CLI sends the active workspace id
by default on commands that work with slugs.
POST /api/v1/posts/:postId/update
Update the current content of a published post.
Request body
{
"markdown": "# Revised title\n\nUpdated body.",
"title": "Revised title",
"visibility": "workspace",
"workspaceId": "ws_123",
"tags": ["api", "v2"],
"sourcePath": "revised-doc.md",
"confirmPublic": false
}
Field semantics are identical to
POST /api/v1/publish, except update does
not apply publish defaults to omitted metadata. markdown and workspaceId
are required. Omit visibility to keep the current visibility, and omit
tags to keep the current tags. Passing tags: [] intentionally clears all
tags. confirmPublic: true is still required if the new visibility is
public.
Success response
{
"postId": "post_123",
"versionId": "ver_456",
"versionNumber": 2,
"slug": "checkout-api-reference",
"title": "Revised title",
"visibility": "workspace",
"tags": ["api", "v2"]
}
Every successful update bumps versionNumber. The slug — and therefore the
URL — does not change.
Errors
| Status | error | When |
|---|---|---|
400 | invalid_request | Body failed validation (empty title+markdown, bad visibility, etc.) |
400 | update_failed | Business-rule failure from the service layer |
404 | not_found | Post ID doesn't exist or isn't owned by the caller |
POST /api/v1/posts/:postId/unpublish
Unpublish a post. The underlying markdown and version history are kept, but the post is no longer reachable at its public URL, and its slot is freed on Free plans.
Success response
{
"postId": "post_123",
"unpublished": true
}
Errors
| Status | error | When |
|---|---|---|
400 | unpublish_failed | Post was already unpublished, or couldn't be unpublished |
404 | not_found | Post ID doesn't exist or isn't owned by the caller |
Re-publishing an unpublished post goes through a fresh
POST /api/v1/publish, which means it goes through
the document-limit check again if the workspace is on Free. update only works
on currently published posts.
POST /api/v1/posts/:postId/visibility
Change post visibility without uploading new Markdown.
Request body
{
"visibility": "public",
"confirmPublic": true
}
visibility must be one of private, workspace, or public.
confirmPublic: true is required when switching to public.
Success response
{
"postId": "post_123",
"visibility": "public"
}
Errors
| Status | error | When |
|---|---|---|
400 | invalid_visibility | visibility wasn't one of the four allowed values |
400 | visibility_change_failed | Most commonly: moving to public without confirmPublic: true |
404 | not_found | Post ID doesn't exist or isn't owned by the caller |
PATCH /api/v1/posts/:postId/tags
Patch tags incrementally without resending the full list.
Request body
{
"add": ["reference"],
"remove": ["draft"]
}
At least one of add or remove must be non-empty. Both accept arrays of
strings; tags are normalized to lowercase and capped at 20 total per post.
Success response
{
"postId": "post_123",
"tags": ["api", "reference"]
}
The response is the final tag list after the patch was applied.
Errors
| Status | error | When |
|---|---|---|
400 | invalid_request | Both add and remove empty, or bad types |
400 | tag_update_failed | Service-layer failure (e.g., exceeding the 20-tag cap) |
404 | not_found | Post ID doesn't exist or isn't owned by the caller |
DELETE /api/v1/posts/:postId
Permanently delete a post and all versions/tags. This is not reversible. It also frees the post slug and, if the post was published, frees a Free-plan document slot.
Success response
{
"postId": "post_123",
"deleted": true
}
Errors
| Status | error | When |
|---|---|---|
400 | delete_failed | Transactional delete failed |
404 | not_found | Post ID doesn't exist or isn't owned by the caller |
Related pages
- Posts endpoints — list, get, raw
- Export endpoints — single and batch export
- Publish endpoints — the initial publish + batch publish
- Errors and pagination — full error catalogue