Concepts
How your website and CMS Orbit work together
The parts involved when a website shows CMS content, what each one does, and what happens when a visitor opens a page, an editor previews and an editor publishes.
Explanation8 min read
CMS Orbit is a headless CMS: editors write content in the CMS, and your website reads it over an API and draws it with your own code and design. This page explains the parts and how they fit, so the step-by-step guides make sense. To build it, follow Connect a website.
The parts
https://api.bifrostorbit.com/cms| Part | Where it runs | What it does |
|---|---|---|
| The editor | In the editor's browser, on CMS Orbit | Where editors write pages and see a preview of your website beside them |
| The delivery API | CMS Orbit, at https://api.bifrostorbit.com/cms | Answers your website's questions with published content, or drafts for previews |
| Your website | Your hosting | Asks the delivery API for content and draws it as HTML with your components |
| The SDK | Inside your website | Makes the calls, caches the answers, draws pages with your components, and handles previews and webhooks |
| Webhooks | CMS Orbit calling your website | Tell your website what changed when editors publish |
| The bridge | A script loaded into your website while previewing | Connects the preview to the editor: sends changes as editors type, and lets them click blocks |
The SDK is optional. Everything it does is a plain HTTP call or a few lines of code, documented in the Delivery API and Connect a website without Next.js.
How content is organised
What your website reads is shaped by how the space is set up:
- A space is one website or app, with its own content, editors and settings.
- Environments, such as Staging and Production, are separate copies of the content. Editors usually work in Staging and promote to Production. A key belongs to one environment and only sees its content.
- Regions, such as Germany and Switzerland, are versions of the website that can have different pages. Each has a key, such as
germany, and may have an address prefix, such as/de. - Languages belong to regions. A region has a default language and may have more, such as
de-DEanden-DE. A language can fall back to another when a page is not translated. - Content types define the kinds of entry, such as a page, a blog post or a footer, and their fields.
- Components are the blocks editors stack on a page, such as a hero or an FAQ list. Each has a key, such as
hero, and fields. - Pages are entries with an address, such as
/products/hiking-boots. The address is stored without the domain or the region prefix.
Your website chooses which environment it reads by the key it uses, and which region and language by what it asks for.
What a page looks like
A page from the delivery API has its own fields, its components and everything needed for SEO:
{ "url": "/products/hiking-boots", "name": "Hiking boots", "contentType": "page", "region": "germany", "locale": "de-DE", "draft": false, "publishedAt": "2026-10-01T09:30:00.000Z", "version": 7, "fields": { "summary": "Boots fitted for your first long walk." }, "components": [ { "id": "6650f1c2a9e4b3d2c1f0ea11", "type": "hero", "schemaVersion": 1, "variant": null, "fields": { "heading": "Find a shop", "image": { "id": "…", "url": "https://…/uploads/…/hero.jpg", "width": 2400, "height": 1600, "alt": "A pair of hiking boots", "mimeType": "image/jpeg", "title": null } }, "children": {} } ], "seo": { "title": "Hiking boots | Acme Outdoors", "description": "…", "canonical": "https://www.example.com/de/products/hiking-boots", "…": "…" }}componentsis a list, in the order editors placed them. Each has anid, atype(the component key),fields, avariantif editors chose one, andchildren: components placed inside it, grouped by slot name.- Images, links and references arrive filled in. An image has its address, size and alt text; a link has its current
href, so renaming a page never breaks links to it. - Rich text is HTML.
seohas the finished title, description, canonical address, Open Graph, Twitter card, hreflang alternates, breadcrumbs and JSON-LD, with defaults applied. The SDK'stoMetadataturns it into Next.js metadata.
The SDK adds meta, which gathers the region, language, fallback and draft details in one place. Field types shows what each field type looks like, and the Delivery API every endpoint.
What happens when a visitor opens a page
- The visitor asks your website for
/products/hiking-boots. - If your website has a fresh rendered copy, it sends it. The CMS is not involved. This is what happens almost every time.
- Otherwise your page route calls
cms.resolve({ path: '/products/hiking-boots' }). The SDK sends GET/resolve?url=/products/hiking-boots®ion=germanywith the public key. - The CMS answers what is at that address: a page, a redirect, gone or not found.
- Your route renders the page with your components, or redirects, or shows the 404 page.
- Next.js keeps the answer and the rendered page, labelled with the page's cache tag,
cms:page:germany:/products/hiking-boots.
If the CMS cannot be reached, the SDK tries again twice, and Next.js keeps serving the copy it has. Caching and refreshing pages explains every cache involved.
What happens when an editor publishes
- The editor selects Publish. The page's draft becomes its published version.
- The CMS sends a signed
POSTto each webhook for that environment, such as{"event":"content.published","data":{"url":"/products/hiking-boots","region":"germany",…}}. - Your webhook route checks the signature and turns the message into cache tags: the page's tag, plus lists, sitemaps and pages that show references.
- Next.js marks those tags as expired. Nothing is fetched yet.
- The next visitor to that page waits a moment while your website fetches the new version and renders it. Everyone after gets the new copy from the cache.
Without a webhook, the change still appears, once the cached copy is older than your revalidate setting.
Draft
What you edit; saves by itself
Review
When the type needs approval
Published
A new version in the history
Delivery API
Cached up to 60 seconds
Website
Webhooks refresh it at once
What happens when an editor previews
- The editor opens a page. The CMS creates a preview token for that draft, valid for an hour, and loads your website in a frame at the Preview URL from the space's settings plus the page's address, such as
https://staging.example.com/products/hiking-boots?preview=eyJ…. - Your proxy sees
?preview=and redirects to your preview route. - The preview route asks the CMS whether the token is valid, turns on Next.js draft mode, which sets a cookie, and redirects back to the page.
- The page route sees draft mode and calls
resolvewithpreview: true. The SDK uses the preview key, addsdraft=trueand skips every cache. - The layout adds the bridge script, because draft mode is on.
- The bridge connects to the editor. As the editor types, it sends the changed page to
CmsLivePage, which redraws it. When the editor clicks a block in the preview, the bridge finds the nearest element withdata-cms-idand tells the editor to select it.
Visitors never have the draft mode cookie, so they never see drafts, never load the bridge and are never slowed down by previews. See Set up the live preview.
Addresses, redirects and gone pages
Your website does not need its own list of pages or redirects. resolve answers for any address:
pageredirecthops says how many.gonenotFound| Answer | When |
|---|---|
page | A page is published at the address, in the region and language asked for, or in a fallback |
redirect | An editor added a redirect from the address, or a page used to be there and moved. Chains are followed for you, and hops says how many. |
gone | An editor marked the address as gone, for a page removed on purpose |
notFound | Nothing is at the address |
When an editor changes a page's address and publishes, the CMS adds a redirect from the old address by itself, and links and menus pointing at the page use the new one.
Gone pages
A gone page tells search engines the page was removed on purpose, so they drop it sooner. The right status is 410. Next.js can only answer 404 from a page, so the SDK's gone() calls notFound() and shows your 404 page. That is enough for most websites.
To answer a true 410, check the address before the page renders, in proxy.ts. This costs one CMS call for every page request, so most websites do not do it:
import { NextResponse, type NextRequest } from 'next/server'import { cms } from '@/lib/cms'export async function proxy(request: NextRequest) { const result = await cms.resolve({ path: request.nextUrl.pathname }).catch(() => null) if (result?.kind === 'gone') return new NextResponse('This page has been removed.', { status: 410 }) return NextResponse.next()}export const config = { matcher: ['/((?!api/|_next/|.*\\..*).*)'] }The proxy runs before Next.js's caches, so this call is made for every request it matches, cached copies included. Keep the matcher as narrow as you can.
What the SDK does and does not do
The SDK does:
- make every delivery API call, with retries, timeouts and typed answers;
- label answers with cache tags and refresh the right ones from webhooks;
- draw pages from your registry of components, and keep the preview live;
- turn SEO fields into metadata, JSON-LD, sitemaps and robots.txt;
- check preview tokens and webhook signatures;
- generate TypeScript types from your schema;
- give you a mock client for tests.
It does not:
- style anything. Every component is yours.
- sanitise rich text. It is HTML written by your editors; see Render components.
- resize images on your server. Images are served by the CMS, which can make smaller copies;
CmsImageasks for them. - search full text.
list({ q })matches entry names. Use a search service for full-text search. - route regions or languages for you. Your folders decide the addresses; see Regions and languages.
- write content. The delivery API is read-only. Scripts that change content use the Management API, and AI agents use MCP.
Was this page helpful?