---
title: "Get a site's FraseCMS delivery token"
method: GET
path: "/sites/{siteId}/cms-token"
tags: ["Sites"]
---

# Get a site's FraseCMS delivery token

`GET /sites/{siteId}/cms-token`

## Request

- Base URL: `https://next.frase.io/api/v1`
- URL: `https://next.frase.io/api/v1/sites/{siteId}/cms-token`
- Auth: API key in header `X-API-KEY`

Returns the site's CURRENT headless delivery token — the bearer credential the public FraseCMS read endpoints accept. Non-mutating: the token is rebuilt (HMAC) from the site's stored secret and version, so repeated calls return the SAME token. It is therefore NOT a show-once secret: as long as the site's secret exists you can fetch the live token again here. Only rotation (POST /sites/{siteId}/cms-token/rotate) changes it, and only revocation removes it. If the token has been revoked (the site's secret was nulled), this returns 200 with `{ "revoked": true }` and nothing else — the endpoint NEVER re-mints a revoked token; re-issue explicitly by rotating. Gates: requires the `write:sites` scope even though the verb is GET, because the response reveals a live credential — a read-only key is refused with 403. Responses carry `Cache-Control: no-store`; never cache or log the token. An org-scoped bucket allows 60 calls per minute per organization on top of your API key's own limits; exceeding it returns a data-wrapped 429 carrying no Retry-After header. Token format is `fcms_<base64url(siteId:version:hmac)>`; `api_endpoint` is the ready-to-call posts URL that embeds it. OPTIONS is supported for CORS preflight (204, `Access-Control-Allow-Methods: GET, OPTIONS`).

## Path parameters

- `siteId` string, uuid, required

## Response `200`

The live delivery token, or the revoked marker. Sent with `Cache-Control: no-store`.

- object
  - `data` union
    - object
      - `token` string: The delivery token, `fcms_` + base64url of `siteId:version:hmac`. Re-derivable: fetching again returns the same string until a rotate or revoke.
      - `version` integer: The site's current delivery-token version. Incremented by every rotation; tokens carrying an older version stop verifying.
      - `api_endpoint` string: Ready-to-call posts endpoint with the token embedded: `<app base URL>/api/cms/<token>/posts`.
      - `revoked` false
    - object: Terminal state: the site's secret was nulled. No token, version, or endpoint is returned and this endpoint will not re-mint one — rotate to re-issue.
      - `revoked` true

## Other responses

- `400`: Validation failed
- `401`: Authentication failed
- `403`: Forbidden - action not allowed on this resource
- `404`: Resource not found
- `422`: FraseCMS is not enabled for this site (`cms_disabled`). Enable the FraseCMS connection first; nothing is minted.
- `429`: Rate limited. Two shapes: the standard API-key limit (Stripe-style error envelope, with Retry-After), or this endpoint's org-scoped mint bucket of 60/minute, which is data-wrapped and carries NO Retry-After.
- `500`: The token could not be read. Data-wrapped, with a fixed message (`Failed to get delivery token`) — v1 routes never leak the underlying error. Nothing was changed.

---

[API](https://skmtc.dev/frase/apis/frase-api.md) · [All operations](https://skmtc.dev/frase/apis/frase-api/llms.txt) · [OpenAPI document](https://skmtc.dev/frase/apis/frase-api/revisions/7fb75d89f7a4?raw)
