Skip to content

Delivery API

Last updated 14 Nov 2026

Read-only HTTP. No key is needed for published content.

{API}/api/v1/{siteId}/{env}/{path}

{env} is normally production. CORS is open, so a browser or a local dev server can call it directly.

Endpoints ​

MethodPathReturns
GET/{route}One document
GET/_partials/{key}One partial
GET/_routesEvery published route and partial
GET/_redirectsPublished redirects
GET/i18n/{locale}Strings for one language
GET/media/{key}File bytes (not under /api/v1)

Get a document ​

GET /api/v1/{siteId}/{env}/about
json
{
  "route": "/about",
  "meta": { "title": "About us", "description": "Who we are." },
  "zones": {
    "main": {
      "blocks": [{ "id": "hero", "type": "brand-hero", "fields": { "title": "…" } }]
    }
  }
}

The root document is the empty path. A leading slash is optional.

Cached public, max-age=0, s-maxage=60.

In another language ​

GET /api/v1/{siteId}/{env}/about?locale=pt-PT

The same document, with its translated text in place of the source text. Nothing else differs: same blocks, same images, same links. Text with no translation in that language keeps the source. locale is matched without regard to case, works on partials too (/_partials/footer?locale=pt-PT), and a language the document has no translations for returns the source document.

A stored document keeps its translations in a translations section. Delivery never returns it, with or without locale.

GET /api/v1/{siteId}/{env}/about?locale=pt-PT&linkPrefix=/pt-pt

For a site that serves each language under its own path. Every link to a page of the site comes back under the prefix: link fields (/about becomes /pt-pt/about, / becomes /pt-pt) and anchors inside rich text. External links, mailto:, same-page #fragments, media files and links to held-back pages are left as they are. The prefix must be a single path segment such as /pt-pt. Anything else is ignored.

Get the route list ​

GET /api/v1/{siteId}/{env}/_routes
json
{
  "siteId": "…",
  "environment": "production",
  "routes": [{ "route": "/about", "type": "page", "updatedAt": "2026-09-01T18:01:27Z" }],
  "partials": ["nav", "footer"]
}

Get redirects ​

GET /api/v1/{siteId}/{env}/_redirects
json
{ "redirects": [{ "from": "/old", "to": "/new", "status": 301 }] }

Merged from both sources: redirect documents, and the meta.redirectsFrom list any document declares. Conflicting and targetless rules are left out. See Redirects.

Get strings ​

GET /api/v1/{siteId}/{env}/i18n/en
json
{ "common.buttons.save": "Save" }

Preview ​

Add a token to any document request:

GET /api/v1/{siteId}/{env}/{route}?previewToken={token}

or send it as X-CMS-Preview-Token. See Preview.

Response headers:

HeaderMeaning
X-CMS-Preview: grantedServing a draft.
X-CMS-Preview: granted; releaseServing a release's content.
X-CMS-Preview: denied; …No valid credential; published content was served.

Status codes ​

CodeMeaning
200Here it is.
400Malformed path.
403A preview token was presented and is invalid, expired, or for another site.
404No such site, environment or document — or it exists and is not public.

A 404 for a hidden document is deliberate: it is indistinguishable from one that does not exist, so unreleased pages cannot be found by probing.

Minting a preview token ​

The one authenticated call your site might make. Requires a console session:

POST /api/preview-token
{ "siteId": "…", "envId": "…", "releaseId": "…" }
json
{ "token": "…", "expiresAt": "2026-09-03T04:00:00Z" }

releaseId is optional. Without it the token previews drafts; with it, that release.

A Nubisco product. Not open source.