Contents
BrainBetaVersion v1

Content Delivery API

Read published pages, collections, menus and redirects of a Nucleo CMS site.

The headless CMS inside Nucleo Brain publishes websites through this read-only API. Your website's server (Next.js, Astro, Nuxt…) fetches the published content of one site: pages resolved by path, collection entries (blog posts, news…), menus, redirects and a sitemap. Drafts are never returned.

Authentication is a per-site delivery token sent as Authorization: Bearer cmsdt_…. A token reads exactly one site. Use it only server-side: the API does not allow cross-origin browser calls and the token must never reach the visitor's browser.

After every publish Nucleo can call your site back with a signed request, retried until your site answers (see Webhooks → cmsRevalidate), so you can refresh your cache.

Authentication

  • deliveryTokenHTTP bearer

    Per-site delivery token (cmsdt_ + 40 characters). Create it in Nucleo: CMS → site → Settings → For developers → Create token (requires the cms.manage permission). The value is shown once; Nucleo stores only its hash. Revoke it from the same card. Only the Authorization header is accepted (no ?token= query parameter).

Base URL
https://api-brain.nucleoplatform.com/api/delivery/v1/{workspace}/sites/{siteId}Production. The full base URL of a site is shown in Nucleo under CMS → *site* → Settings → For developers.
Who calls it
Your servers
Endpoints
8 + 1 webhook
OpenAPI 3.1 specification
content.yaml
{workspace} — Slug of your Nucleo Brain workspace (lowercase letters, digits and dashes). (examples use acme; full URL https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12)
{siteId} — Numeric id of the CMS site. (examples use 12; full URL https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12)

Site

Basic data of the site the token belongs to.

get/

Get the site

Id, slug, name, status, default locale, enabled locales and the public URL of the site's frontend (as configured in Nucleo; null if not set).

Rate limit (all endpoints): 120 requests per minute per IP address.

GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/
Authentication: Bearer token
Rate limit: 120 requests per 1m, per IP

Responses

  • 200The site.
    • dataSite
      Child attributes
      • idinteger
      • slugstring
      • namestring
      • statusstring
      • default_localestring
      • localesarray of string
      • frontend_urlstring (uri) | null
  • 401Missing or unknown delivery token.
    • messagestring
  • 404Not found. Also returned when the workspace slug is unknown or when the token belongs to another site (Nucleo never confirms that a site exists to a token that cannot read it).
    • messagestring
  • 429More than 120 requests in a minute from the same IP.
    Headers
    • Retry-After
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • messagestring
Request
curl -X GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/ \
  -H "Authorization: Bearer $NUCLEO_ACCESS_TOKEN" \
  -H 'Accept: application/json'
Response
{
  "data": {
    "id": 12,
    "slug": "acme-website",
    "name": "Acme Apparel website",
    "status": "active",
    "default_locale": "en",
    "locales": [
      "en",
      "it"
    ],
    "frontend_url": "https://www.acme.example"
  }
}
WebhookcmsRevalidate

Content published on the site

Sent by Nucleo right after a page or an entry is published, to the Revalidate URL configured in CMS → site → Settings, signed with the site's Revalidate secret.

  • Page published: path is / (refresh the whole site).
  • Entry published: path is /<collection-slug>.

Signature. Nucleo-Signature: t=<unix time>,v1=<signature>, where the signature is the lowercase hex HMAC-SHA256 of <t>.<raw body> keyed with the Revalidate secret. Verify it on the exact bytes received, compare in constant time and reject a t older than 5 minutes. Without a secret on the site the request is not signed. The secret body field is deprecated: it is still sent for older receivers and will be removed.

Retries. 5-second timeout, redirects not followed. Any 2xx is a success (the body is ignored); any other answer or no answer is retried after 1 min, 5 min, 30 min, 2 h and 6 h — at most 6 attempts, then the delivery is dropped and logged. Every attempt has the same id (also in Nucleo-Webhook-Id) and its number in Nucleo-Webhook-Attempt, so you can ignore duplicates.

Headers

  • Nucleo-Signaturestring

    t=<unix time>,v1=<hex HMAC-SHA256 of <t>.<raw body>> keyed with the Revalidate secret. Absent when the site has no secret.

    Example: t=1791115200,v1=5f2b6c0e9a8d4f7b1c3e2a6d9b0f8e7c6a5d4b3c2e1f0a9b8c7d6e5f4a3b2c1d
  • Nucleo-Webhook-Idstring (uuid)required

    Delivery id, the same on every attempt (equals id in the body).

    Example: 3f6c1a52-8e0b-4d7a-9c2e-1b5d7f9a0c34
  • Nucleo-Webhook-Eventstringrequired
    One ofcontent.published
    Example: content.published
  • Nucleo-Webhook-Attemptintegerrequired

    Attempt number, from 1 to 6.

    min 1max 6
    Example: 1

Request Nucleo sendsapplication/json

  • idstring (uuid)required

    Delivery id

  • eventstringrequired
    One ofcontent.published
  • sitestringrequired

    Site slug.

  • localestringrequired
  • pathstringrequired
  • published_atstring (date-time)required

    When the content was published.

  • secretstringdeprecated

    The Revalidate secret of the site (empty string if not set). Deprecated, verify Nucleo-Signature instead.

Expected response

  • 200Acknowledged. Any 2xx stops the retries.
Body
{
  "id": "3f6c1a52-8e0b-4d7a-9c2e-1b5d7f9a0c34",
  "event": "content.published",
  "site": "acme-website",
  "locale": "en",
  "path": "/blog",
  "published_at": "2026-10-04T12:00:00+00:00",
  "secret": "whsec_example_123"
}
Response
HTTP 200 — Acknowledged. Any `2xx` stops the retries.

Pages

Pages resolved by URL path and locale.

get/page

Get a published page by path

Resolves path segment by segment through the translated slugs of the page tree in the given locale. / (or no path) returns the page marked as home. Returns 404 when no page matches, when the page has no translation in that locale, or when that translation is not published.

blocks and seo are returned exactly as edited in Nucleo (free-form JSON defined by the page template): render them with your own components.

GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/page
Authentication: Bearer token
Rate limit: 120 requests per 1m, per IP

Query parameters

  • pathstring

    URL path, with or without leading/trailing slashes. Default /.

    default /
    Example: /about/team
  • localestring

    Locale code enabled on the site. Default the site's default locale.

    Example: en

Responses

  • 200The page.
    • dataPage
      Child attributes
      • idinteger
      • templatestring
      • is_homeboolean
      • titlestring
      • slugstring
      • blocksarray of any | object | null

        Page content as edited in Nucleo (template-defined JSON).

      • seoobject | null
      • localestring
      • published_atstring (date-time) | null
  • 401Missing or unknown delivery token.
    • messagestring
  • 404Page not found, not translated or not published (or wrong site/workspace).
    • messagestring
  • 429More than 120 requests in a minute from the same IP.
    Headers
    • Retry-After
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • messagestring
Request
curl -X GET 'https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/page?path=%2Fabout%2Fteam&locale=en' \
  -H "Authorization: Bearer $NUCLEO_ACCESS_TOKEN" \
  -H 'Accept: application/json'
Response
{
  "data": {
    "id": 87,
    "template": "default",
    "is_home": false,
    "title": "Our team",
    "slug": "team",
    "blocks": [
      {
        "type": "hero",
        "props": {
          "heading": "Meet the people behind Acme Apparel",
          "media_id": 311
        }
      },
      {
        "type": "rich_text",
        "props": {
          "html": "<p>Designed in Milano since 2009.</p>"
        }
      }
    ],
    "seo": {
      "title": "Our team · Acme Apparel",
      "description": "The people behind Acme Apparel."
    },
    "locale": "en",
    "published_at": "2026-09-30T10:15:00.000000Z"
  }
}

Collections

Entries of a collection (blog, news, case studies…), paginated.

get/collections/{collection}/entries

List published entries of a collection

Entries that have a published translation in the locale, newest first (by publish date, then id). Standard pagination: data plus current_page, last_page, per_page, total, links and the page URLs. Unknown collection → 404.

GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/collections/{collection}/entries
Authentication: Bearer token
Rate limit: 120 requests per 1m, per IP

Path parameters

  • collectionstringrequired

    Collection slug.

    Example: blog

Query parameters

  • localestring

    Locale code enabled on the site. Default the site's default locale.

    Example: en
  • pageinteger
    min 1default 1
    Example: 1
  • per_pageinteger

    Clamped to 1–50.

    min 1max 50default 12
    Example: 12

Responses

  • 200A page of entries.
    • current_pageinteger
    • dataarray of Entry & object
      Attributes of each item
      • idinteger
      • titlestring
      • slugstring
      • fieldsobject | null

        Collection-defined fields.

      • seoobject | null
      • published_atstring (date-time) | null
      • sortinteger
    • first_page_urlstring (uri)
    • frominteger | null
    • last_pageinteger
    • last_page_urlstring (uri)
    • linksarray of object
    • next_page_urlstring (uri) | null
    • pathstring (uri)
    • per_pageinteger
    • prev_page_urlstring (uri) | null
    • tointeger | null
    • totalinteger
  • 401Missing or unknown delivery token.
    • messagestring
  • 404Not found. Also returned when the workspace slug is unknown or when the token belongs to another site (Nucleo never confirms that a site exists to a token that cannot read it).
    • messagestring
  • 429More than 120 requests in a minute from the same IP.
    Headers
    • Retry-After
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • messagestring
Request
curl -X GET 'https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/collections/blog/entries?locale=en&page=1&per_page=12' \
  -H "Authorization: Bearer $NUCLEO_ACCESS_TOKEN" \
  -H 'Accept: application/json'
Response
{
  "current_page": 1,
  "data": [
    {
      "id": 501,
      "sort": 0,
      "title": "Autumn drop is live",
      "slug": "autumn-drop",
      "fields": {
        "excerpt": "Ten new tees in organic cotton.",
        "cover_media_id": 340
      },
      "seo": {
        "title": "Autumn drop · Acme Apparel"
      },
      "published_at": "2026-09-28T08:00:00.000000Z"
    }
  ],
  "first_page_url": "https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/collections/blog/entries?page=1",
  "from": 1,
  "last_page": 4,
  "last_page_url": "https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/collections/blog/entries?page=4",
  "links": [],
  "next_page_url": "https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/collections/blog/entries?page=2",
  "path": "https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/collections/blog/entries",
  "per_page": 12,
  "prev_page_url": null,
  "to": 12,
  "total": 41
}
get/collections/{collection}/entries/{entry}

Get a published entry by slug

One entry by its translated slug in the locale; 404 if it does not exist or is not published in that locale.

GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/collections/{collection}/entries/{entry}
Authentication: Bearer token
Rate limit: 120 requests per 1m, per IP

Path parameters

  • collectionstringrequired

    Collection slug.

    Example: blog
  • entrystringrequired

    Entry slug in the requested locale.

    Example: autumn-drop

Query parameters

  • localestring

    Locale code enabled on the site. Default the site's default locale.

    Example: en

Responses

  • 200The entry.
    • dataEntry
      Child attributes
      • idinteger
      • titlestring
      • slugstring
      • fieldsobject | null

        Collection-defined fields.

      • seoobject | null
      • published_atstring (date-time) | null
  • 401Missing or unknown delivery token.
    • messagestring
  • 404Not found. Also returned when the workspace slug is unknown or when the token belongs to another site (Nucleo never confirms that a site exists to a token that cannot read it).
    • messagestring
  • 429More than 120 requests in a minute from the same IP.
    Headers
    • Retry-After
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • messagestring
Request
curl -X GET 'https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/collections/blog/entries/autumn-drop?locale=en' \
  -H "Authorization: Bearer $NUCLEO_ACCESS_TOKEN" \
  -H 'Accept: application/json'
Response
{
  "data": {
    "id": 501,
    "title": "Autumn drop is live",
    "slug": "autumn-drop",
    "fields": {
      "excerpt": "Ten new tees in organic cotton.",
      "body": "<p>Available from today in Milano and online.</p>",
      "cover_media_id": 340
    },
    "seo": {
      "title": "Autumn drop · Acme Apparel",
      "description": "Ten new tees in organic cotton."
    },
    "published_at": "2026-09-28T08:00:00.000000Z"
  }
}

Navigation

Menus, redirects and sitemap — everything a frontend needs to build routes.

get/menus

List the site's menus

Every menu of the site with its items, as edited in Nucleo (items are free-form JSON).

GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/menus
Authentication: Bearer token
Rate limit: 120 requests per 1m, per IP

Responses

  • 200Menus.
    • dataarray of Menu
      Attributes of each item
      • slugstring
      • itemsarray of object | null
  • 401Missing or unknown delivery token.
    • messagestring
  • 404Not found. Also returned when the workspace slug is unknown or when the token belongs to another site (Nucleo never confirms that a site exists to a token that cannot read it).
    • messagestring
  • 429More than 120 requests in a minute from the same IP.
    Headers
    • Retry-After
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • messagestring
Request
curl -X GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/menus \
  -H "Authorization: Bearer $NUCLEO_ACCESS_TOKEN" \
  -H 'Accept: application/json'
Response
{
  "data": [
    {
      "slug": "main",
      "items": [
        {
          "label": "Shop",
          "url": "/shop"
        },
        {
          "label": "Journal",
          "url": "/blog"
        },
        {
          "label": "About",
          "url": "/about"
        }
      ]
    }
  ]
}
get/redirects

List the site's redirects

Redirect rules to apply in your frontend or edge (code is the HTTP status, e.g. 301 or 302).

GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/redirects
Authentication: Bearer token
Rate limit: 120 requests per 1m, per IP

Responses

  • 200Redirects.
    • dataarray of Redirect
      Attributes of each item
      • from_pathstring
      • to_pathstring
      • codeinteger
        One of301302307308
      • localestring | null
  • 401Missing or unknown delivery token.
    • messagestring
  • 404Not found. Also returned when the workspace slug is unknown or when the token belongs to another site (Nucleo never confirms that a site exists to a token that cannot read it).
    • messagestring
  • 429More than 120 requests in a minute from the same IP.
    Headers
    • Retry-After
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • messagestring
Request
curl -X GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/redirects \
  -H "Authorization: Bearer $NUCLEO_ACCESS_TOKEN" \
  -H 'Accept: application/json'
Response
{
  "data": [
    {
      "from_path": "/chi-siamo",
      "to_path": "/it/about",
      "code": 301,
      "locale": "it"
    },
    {
      "from_path": "/summer-sale",
      "to_path": "/shop",
      "code": 302,
      "locale": null
    }
  ]
}
get/sitemap

List every published path

Every published path for every enabled locale: pages (only when the page and all its ancestors are translated in that locale, so the path is resolvable by /page?path=) and collection entries (/<collection>/<entry-slug>). Use it to generate sitemap.xml and static routes.

GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/sitemap
Authentication: Bearer token
Rate limit: 120 requests per 1m, per IP

Responses

  • 200Published paths.
    • dataarray of SitemapItem
      Attributes of each item
      • localestringrequired
      • kindstringrequired
        One ofpageentry
      • collectionstring

        Only for kind = entry.

      • pathstringrequired
      • updated_atstring (date-time) | null
  • 401Missing or unknown delivery token.
    • messagestring
  • 404Not found. Also returned when the workspace slug is unknown or when the token belongs to another site (Nucleo never confirms that a site exists to a token that cannot read it).
    • messagestring
  • 429More than 120 requests in a minute from the same IP.
    Headers
    • Retry-After
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • messagestring
Request
curl -X GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/sitemap \
  -H "Authorization: Bearer $NUCLEO_ACCESS_TOKEN" \
  -H 'Accept: application/json'
Response
{
  "data": [
    {
      "locale": "en",
      "kind": "page",
      "path": "/",
      "updated_at": "2026-09-30T10:15:00.000000Z"
    },
    {
      "locale": "en",
      "kind": "page",
      "path": "/about/team",
      "updated_at": "2026-09-30T10:15:00.000000Z"
    },
    {
      "locale": "en",
      "kind": "entry",
      "collection": "blog",
      "path": "/blog/autumn-drop",
      "updated_at": "2026-09-28T08:00:00.000000Z"
    }
  ]
}

Media

Resolve a media id to its public URL.

get/media/{mediaId}

Resolve a media id to its URL

302 redirect to the stable public URL of a media file of this site. Meant for your server to turn a media id found in blocks/fields into a URL. Never use this endpoint as src of an <img>/<video>: the browser would need the token. Put the Location URL in your HTML instead.

GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/media/{mediaId}
Authentication: Bearer token
Rate limit: 120 requests per 1m, per IP

Path parameters

  • mediaIdintegerrequired
    Example: 311

Responses

  • 302Redirect to the public file.
    Headers
    • Location
  • 401Missing or unknown delivery token.
    • messagestring
  • 404Not found. Also returned when the workspace slug is unknown or when the token belongs to another site (Nucleo never confirms that a site exists to a token that cannot read it).
    • messagestring
  • 429More than 120 requests in a minute from the same IP.
    Headers
    • Retry-After
    • X-RateLimit-Limit
    • X-RateLimit-Remaining
    • messagestring
Request
curl -X GET https://api-brain.nucleoplatform.com/api/delivery/v1/acme/sites/12/media/311 \
  -H "Authorization: Bearer $NUCLEO_ACCESS_TOKEN"
Response
{
  "message": "Delivery token required (Authorization: Bearer)."
}