Publishing API v2

Use v2 for new article integrations. Draft upload and final submission are separate operations. Public reading and channel administration retain their existing APIs.

Quick start

Create a draft with its primary translation, upload images with page_slug, save the other translations in batches, verify them, then submit status with the exact expected_languages set. A 202 response means review has started, not that publication is complete.

POST /api/v2/channels/demo_channel/pages
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Idempotency-Key: article-2026-01:create

{
  "slug": "example_article",
  "default_language": "en",
  "translations": [{
    "language": "en",
    "title": "Example article",
    "excerpt": "A separate summary.",
    "content_markdown": "Introduction.\n\n## Section\n\nBody."
  }]
}

Each JSON write accepts at most 20 translations and 2 MiB (2,097,152 bytes), both limits apply. An article supports up to 700 translation records; this is a capacity limit, not a promise of 700 supported languages. Oversized requests return 413 before saving anything.

PUT /api/v2/channels/demo_channel/pages/example_article/translations
Idempotency-Key: article-2026-01:batch-2
Content-Type: application/json

{"translations":[{"language":"fr","title":"Exemple","content_markdown":"Introduction.\n\n## Section\n\nTexte."}]}

PATCH /api/v2/channels/demo_channel/pages/example_article/status
Idempotency-Key: article-2026-01:publish
Content-Type: application/json

{"status":"published","expected_languages":["en","fr"]}

GET article returns a compact summary. GET translations returns metadata with cursor pagination (up to 100 per page). GET translations/{language} returns one body; formats[]=html and formats[]=markdown select formats. view=working reads a pending revision without changing the public version.

content_sha256 hashes saved normalized HTML, not the input Markdown. payload_sha256 covers title, excerpt, HTML, SEO fields and og_image_path. revision increments when that content changes, not when status changes. A hash does not replace quality and image checks.

API v2

GET /api/v2/channels
GET /api/v2/channels/{channel}/pages
POST /api/v2/channels/{channel}/pages
GET /api/v2/channels/{channel}/pages/{article}
GET /api/v2/channels/{channel}/pages/{article}/translations
PUT /api/v2/channels/{channel}/pages/{article}/translations
GET /api/v2/channels/{channel}/pages/{article}/translations/{language}
PUT /api/v2/channels/{channel}/pages/{article}/translations/{language}
PATCH /api/v2/channels/{channel}/pages/{article}/status
PATCH /api/v2/channels/{channel}/pages/{article}/review
PATCH /api/v2/channels/{channel}/pages/{article}/settings
POST /api/v2/channels/{channel}/media

Idempotency-Key

Every v2 write requires Idempotency-Key. Retry the same method, path and identical JSON bytes with the same key. A changed request needs a new key; reusing a key for different data returns 409. The replay returns the original result; use GET for current status.

Article lifecycle

Bulk review runs in batches of 20 on a lower-priority queue. GET exposes progress and manual-review state. PATCH review with paused=true pauses new work; paused=false resumes or recovers pending work. An already running provider request may finish. Notifications wait for the publication set.

v1 → v2

Migration: keep v1 clients unchanged until their verifier supports v2. Replace the final full GET with summary, paginated metadata and individual body checks. Persist progress and keys. Split 413 batches; if one translation is too large, stop. Honor Retry-After on 429; never blindly repeat publication.

API v1 remains supported with full responses. It is not deprecated and has no retirement date. API v1

Public Content API

The Public Content API is a separate read-only interface. It needs no API key and returns only published channels, articles, and translations.

GET /api/public/v1/channels
GET /api/public/v1/search?q=publishing

OpenAPI schema · llms.txt