$ cli-blog docs

Revisions and redirects

Inspect post revision snapshots and resolve old slugs after a post URL changes.

Revisions preserve post snapshots for audit, comparison, and restore workflows. Slug redirects map an old locale-scoped slug to the post's current slug and recommended HTTP status.

Choose a surface

The API, Node SDK, and CLI expose the same revision and redirect records. Compare all Cli Blog tools.

ActionHTTP APINode SDKCLIDescription
List revisionsGET /v1/posts/{id}/revisionsblog.posts.revisions.list() or .paginate()cli-blog posts revisions listPage through stored snapshots
Retrieve revisionGET /v1/posts/{id}/revisions/{revisionId}blog.posts.revisions.get()cli-blog posts revisions getRead one snapshot with Markdown
Resolve old slugGET /v1/posts/slug-redirects/{slug}blog.posts.slugRedirects.get()cli-blog posts redirects getFind the current slug and redirect status

Revision reads require a private key with editorial post access. Public and private keys with post read access can resolve slug redirects.

Use the REST API

Call the resource from any technology that can send HTTP requests. Use a public key for allowed delivery reads and a private key for content changes.

GET https://api.cli-blog.com/v1/posts/{id}/revisions
Choose an API key

Use the CLI

Use the CLI for local work, continuous integration, and JSON automation. Configure a narrowly scoped key before running content-changing commands.

cli-blog posts revisions list post_id --json
Read the CLI guide

Use the Node SDK

Use the Node SDK in Node.js 20+ servers, scripts, continuous integration, and trusted agent environments. Its methods map to the same resource actions.

await blog.posts.revisions.list(postId)
Read the Node SDK guide

Use the agent skill

Give the agent the Cli Blog skill and a result-focused instruction. The skill helps it choose the API, CLI, or SDK and keeps publishing behind an approval gate.

List revisions for the selected post and summarize what changed. Do not restore a version without approval.
Install the agent skill

List post revisions

Cursor pagination is the preferred list mode. Set limit up to 100, then pass next_cursor as after. Use numbered pagination only when a revision browser requires exact totals. See Pagination.

curl --get \
  "https://api.cli-blog.com/v1/posts/post_2f51de/revisions" \
  --header "x-api-key: $CLI_BLOG_PRIVATE_API_KEY" \
  --data-urlencode "locale=en-US" \
  --data-urlencode "limit=20"

The list returns snapshot metadata without the full Markdown body:

{
  "object": "list",
  "data": [{
    "id": "rev_4b11ac",
    "object": "post_revision",
    "parent_post_id": "post_2f51de",
    "title": "How we ship release notes",
    "version": 1,
    "created_at": "2026-07-11T17:20:00.000Z",
    "updated_at": "2026-07-11T17:20:00.000Z"
  }],
  "has_more": false,
  "next_cursor": null
}

The SDK and CLI support the same cursor controls:

import { CliBlog } from "@cli-blog/node";

const blog = new CliBlog({
  apiKey: process.env.CLI_BLOG_PRIVATE_API_KEY!,
  apiUrl: "https://api.cli-blog.com",
});

const revisions = await blog.posts.revisions.list("post_2f51de", {
  locale: "en-US",
  limit: 20,
});
cli-blog posts revisions list post_2f51de \
  --locale en-US \
  --limit 20 \
  --json

Retrieve and restore a revision

Retrieve one snapshot when the workflow needs its Markdown body:

curl --get \
  "https://api.cli-blog.com/v1/posts/post_2f51de/revisions/rev_4b11ac" \
  --header "x-api-key: $CLI_BLOG_PRIVATE_API_KEY" \
  --data-urlencode "locale=en-US"
{
  "id": "rev_4b11ac",
  "object": "post_revision",
  "parent_post_id": "post_2f51de",
  "title": "How we ship release notes",
  "version": 1,
  "body_markdown": "## A repeatable release workflow\n\nDraft, review, then publish.",
  "created_at": "2026-07-11T17:20:00.000Z",
  "updated_at": "2026-07-11T17:20:00.000Z"
}

Cli Blog does not expose a separate restore endpoint. Read the snapshot, review the diff, then send the selected fields through the normal post update action.

const revision = await blog.posts.revisions.get(
  "post_2f51de",
  "rev_4b11ac",
  { locale: "en-US" },
);

const current = await blog.posts.get("post_2f51de", {
  locale: "en-US",
  fields: ["workflow"],
});

await blog.posts.update("post_2f51de", {
  title: revision.title,
  body_markdown: revision.body_markdown,
  expected_version: current.version,
});
cli-blog posts revisions get post_2f51de rev_4b11ac --locale en-US --json

Resolve an old slug

Resolve the slug before rendering a missing-post page. Send the returned HTTP status and redirect to the current slug on your own domain.

curl --get \
  "https://api.cli-blog.com/v1/posts/slug-redirects/old-release-workflow" \
  --header "x-api-key: $CLI_BLOG_PUBLIC_API_KEY" \
  --data-urlencode "locale=en-US"
{
  "object": "slug_redirect",
  "content_type": "blog_post",
  "locale": "en-US",
  "from_slug": "old-release-workflow",
  "to_slug": "how-we-ship-release-notes",
  "post_id": "post_2f51de",
  "status_code": 301
}
const redirect = await blog.posts.slugRedirects.get(
  "old-release-workflow",
  { locale: "en-US" },
);
cli-blog posts redirects get old-release-workflow --locale en-US --json

Revision and redirect fields

ResourceFields and description
Revision summaryid, object, parent_post_id, title, version, created_at, and updated_at identify a snapshot
Revision detailAdds body_markdown for comparison or a reviewed restore update
Slug redirectlocale, from_slug, to_slug, post_id, and status_code describe the redirect target

Common errors

StatusCauseResolution
400Unsupported locale or mixed pagination modesSelect an enabled locale and use one pagination mode
401Missing or invalid API keySend the organization key in x-api-key
403Public key used for revisions or key lacks post permissionUse a private key for revisions and scoped read permission
404Post, revision, old slug, or localized variant does not existConfirm identifiers, organization, and locale

Complete API operations

The operation reference lists every path and query parameter, revision schema, redirect schema, authentication rule, and error response. Use Try it to open the request runner.

List revisions

Key type: Private key

Lists stored revision snapshots for a localized post entry. Cursor pagination is the default; page and per_page opt into exact numbered pagination and cannot be combined with after or limit.

Authorization

ApiKeyAuth
x-api-key<token>

Organization API key. Each key selects exactly one organization; do not send an organization ID separately. Public keys use the cli_blog_pk_ prefix and are intended for published-content delivery reads. Private keys use the cli_blog_sk_ prefix, belong only in trusted environments, and are required for write, publish, delete, and editorial-state workflows when their scopes allow it.

In: header

Path Parameters

id*string

Post ID or locale-scoped post slug.

Query Parameters

after?string

Opaque cursor returned by the previous revision list page.

limit?|

Maximum post revisions to return. Defaults to 20. The maximum accepted value is 100.

page?|

One-based exact page number. Supplying page selects numbered pagination and cannot be combined with after or limit.

per_page?|

Items per numbered page. Requires page, defaults to 20, and cannot be combined with after or limit.

locale?string

Post locale for slug lookup. Omit it to use the organization primary/default locale.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/posts/string/revisions"
{  "object": "list",  "has_more": true,  "next_cursor": "string",  "page": 0,  "per_page": 0,  "total_items": 0,  "total_pages": 0,  "data": [    {      "id": "string",      "object": "post_revision",      "parent_post_id": "string",      "title": "string",      "version": 0,      "created_at": "string",      "updated_at": "string"    }  ]}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}

Retrieve a revision

Key type: Private key

Retrieves one revision snapshot for Markdown diff and restore workflows.

Authorization

ApiKeyAuth
x-api-key<token>

Organization API key. Each key selects exactly one organization; do not send an organization ID separately. Public keys use the cli_blog_pk_ prefix and are intended for published-content delivery reads. Private keys use the cli_blog_sk_ prefix, belong only in trusted environments, and are required for write, publish, delete, and editorial-state workflows when their scopes allow it.

In: header

Path Parameters

id*string

Post ID or locale-scoped post slug.

revisionId*string

Stored post revision ID.

Query Parameters

locale?string

Post locale for slug lookup. Omit it to use the organization primary/default locale.

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/posts/string/revisions/string"
{  "id": "string",  "object": "post_revision",  "parent_post_id": "string",  "title": "string",  "version": 0,  "created_at": "string",  "updated_at": "string",  "body_markdown": "string"}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}

Resolve an old slug

Key type: Public or private key

Resolve an old locale-scoped post slug after a slug change. Customer sites can use the returned to_slug and status_code to issue their own redirect on the public blog domain.

Authorization

ApiKeyAuth
x-api-key<token>

Organization API key. Each key selects exactly one organization; do not send an organization ID separately. Public keys use the cli_blog_pk_ prefix and are intended for published-content delivery reads. Private keys use the cli_blog_sk_ prefix, belong only in trusted environments, and are required for write, publish, delete, and editorial-state workflows when their scopes allow it.

In: header

Path Parameters

slug*string

Previous locale-scoped post slug.

Query Parameters

locale?string

Locale where the old slug was valid. Omit it to use the organization primary/default locale.

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/posts/slug-redirects/string"
{  "object": "slug_redirect",  "content_type": "blog_post",  "locale": "string",  "from_slug": "string",  "to_slug": "string",  "post_id": "string",  "status_code": 0}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}
{  "error": {    "code": "invalid_request",    "message": "string",    "param": "string"  }}

On this page