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.

7 min read

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:

KindStarts withMayBelongs to
Deliverycms_d_Read published content from the delivery APIThe environment
Previewcms_p_Read drafts tooThe environment
Managementcms_m_With the model scope, sync the content model; with content, write contentThe 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:

TokenStarts withMade whenLasts
Canvascms_v_The canvas opens your site, for the person looking at it8 hours
Sitecms_s_An editor signs in on your site8 hours
Formcms_f_The prompt bar's form opens on your siteAs 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:

ContentReads as
Offrefused
Publisheda delivery token
Published and draftsa delivery token, or a preview token on requests that ask for drafts
Managementthe 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 route

Without 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.

On this page

Marks in the margin

  • Note
  • Watch out
  • Updated

Scribes marked doubtful lines in the margin. That practice is called obelism.

Stuck on a step?