Guides · Administration
API tokens
Delivery, preview and management tokens, and the tokens Content makes for you; what each may read or change, creating and revoking them, and the environment variables a site uses.
Your site, your build and your scripts reach Content with a token. A token belongs to one space and one environment, so a production site uses a production token, and a develop token sees nothing of production.
Kinds
You create three kinds under Settings → API tokens:
| Kind | Starts with | May | Belongs to |
|---|---|---|---|
| Delivery | cms_d_ | Read published content from the delivery API | The environment |
| Preview | cms_p_ | Read drafts too | The environment |
| Management | cms_m_ | With the model scope, sync the content model; with content, write content | The person who created it |
- Delivery tokens are made for being seen: CORS is open, so a page can read published content with one from the browser.
- Preview tokens see what isn't published yet. Keep them on your server: from a browser they only work on the environment's preview URL.
- Management tokens act as the person who created them, with that person's role at the time of each request: they stop working when the person leaves the organization, and can't do more than the person may. Pick what one may do under What it may do: the content model (the CLI and the management API) and content (the agent API's content routes, for migrations, imports and backups). The delivery API refuses them.
Content makes more tokens itself, which you never create or copy:
| Token | Starts with | Made when | Lasts |
|---|---|---|---|
| Canvas | cms_v_ | The canvas opens your site, for the person looking at it | 8 hours |
| Site | cms_s_ | An editor signs in on your site | 8 hours |
| Form | cms_f_ | The prompt bar's form opens on your site | As its site token |
Canvas and site tokens read drafts as their person, like a preview token with that person's role, and are refused from a browser on any origin but the preview URL.
Creating and revoking
Admins and developers create tokens under Settings → API tokens: a name (what it is for, like "Vercel production"), a kind and, for a management token, its scopes. The token is shown once, right after it is created: copy it into your host's secrets or an untracked .env file. Only a hash of it is stored, so a lost token can't be shown again; create a new one.
Every token expires: in 30 days, 90 days, 1 year or never, picked under Expires when you create it. 1 year is the default, and creating a token without picking asks you to confirm that date first. After it, the token is refused with 401 and token_expired: create a new one, put it where the old one was, and revoke the old one. The list marks a token that expires within 14 days, and one unused for 90 days, which is likely forgotten and safe to revoke. The organization's admins and the person who created the token get an email 14 and 3 days before it expires.
Each token lists when it was last used. Revoke stops it on the next request, or within 60 seconds for a delivery token answered from the edge cache. Revoking is recorded in Activity, never the token itself.
Agents list, create and revoke tokens with listApiTokens, createApiToken and revokeApiToken, in develop and staging. Production tokens are made by a person in the dashboard.
Without a stored token
A deployment on Vercel, or a GitHub Actions workflow, can use the OIDC token its host gives it instead of an API token: nothing to store, rotate or let expire. Connect it under Connected deployments on the space's page on www.obelism.studio: its Vercel team and project ids, or its GitHub repository id, and a rule per environment of the deployment. Each rule maps that environment (a Vercel environment like production or preview, a GitHub environment or ref, or any for the rest) to one environment of the space, with what Content allows there:
| Content | Reads as |
|---|---|
| Off | refused |
| Published | a delivery token |
| Published and drafts | a delivery token, or a preview token on requests that ask for drafts |
| Management | the above, and a management token with its scopes, acting as you |
Oxygen issues no OIDC token, so a storefront on Shopify Oxygen uses a stored delivery or preview token instead.
Then pass the SDK a token source:
import { ContentClient } from '@obelism/content'
import { vercelOidc } from '@obelism/content/oidc' // needs @vercel/oidc installed
const content = new ContentClient({
token: vercelOidc(), // or githubOidc() in a workflow with `permissions: id-token: write`
space: process.env.CONTENT_SPACE_ID,
environment: process.env.CONTENT_ENVIRONMENT,
})
const draft = await content.getPage(path, { drafts: true }) // a preview routeWithout the SDK, request the host's token for https://content.obelism.studio and send it as the bearer token.
The token's environment picks the rule, so a site's preview deployments read the environment their rule maps them to. A site that names its space and environment (Obelism-Space and Obelism-Environment, the SDK's space and environment) gets 401 with token_place_mismatch from a deployment whose rule maps it elsewhere. Drafts are only read when a request asks for them with Obelism-Drafts: true (the SDK's drafts: true), so a site's pages and its preview can share one connection without a page showing a draft.
A token no connection lets in gets 401 with no_connection; one whose environment has no rule, no_rule; one whose rule has Content off, product_off. Asking for drafts with a rule that reads published content only gets 403 with drafts_not_allowed. A project connected to more than one space names the one it means with the Obelism-Space header, or gets 401 with ambiguous_space.
Delivery requests with a host token skip the edge cache, since the token changes every hour or two; they are still answered from the content cache.
Local development
vercelOidc() works on your own machine too. Link the repository to the Vercel project with vercel link and sign in with vercel login: the SDK takes the token vercel env pull wrote to your .env, or asks Vercel for a new one with your sign-in when there is none or it expired. That token's environment is development, so the connection needs a rule for development (its template maps it to develop), and CONTENT_ENVIRONMENT names the same environment. Only members of the Vercel team get a token: anyone else, and CI outside Vercel and GitHub Actions, uses a delivery token.
Sending one
curl -H "Obelism-Token: cms_d_…" \
"https://content.obelism.studio/api/delivery/v1/pages?path=home"The delivery API takes a token in Obelism-Token or as Authorization: Bearer …; only Obelism-Token lets a delivery token be answered from the edge cache. The management, agent and site APIs take Authorization: Bearer …. The SDK sends the right one.
Environment variables
Settings → API tokens → Copy as .env copies what a site needs to reach the space in that environment, for every product it uses, grouped per product:
# Improve
IMPROVE_ORGANIZATION_ID=org_…
IMPROVE_SPACE_ID=space_…
IMPROVE_ENVIRONMENT=production
IMPROVE_API_TOKEN="<from Improve › API tokens>"
# Content
CONTENT_SPACE_ID=space_…
CONTENT_ENVIRONMENT=production
CONTENT_DELIVERY_TOKEN="<from Content › API tokens>"
# Numbers
NUMBERS_ORGANIZATION_ID=org_…
NUMBERS_SPACE_ID=space_…
NUMBERS_ENVIRONMENT=production
NUMBERS_API_TOKEN="<from Numbers › API tokens on Setup>"A token line names the page to make one on, since a token is only shown when it is created.
When a connected deployment reaches the environment, Copy for connected deployments next to it copies the same lines without the token lines of the products it reaches, since the deployment sends its own.
With a Shopify store connected, it adds SHOPIFY_STORE_DOMAIN and SHOPIFY_STOREFRONT_TOKEN under Content. The SDK and npx @obelism/content init use the same names, plus CONTENT_PREVIEW_TOKEN for drafts and CONTENT_MANAGEMENT_TOKEN for the CLI.