Headless CMS platform
A simple publishing platform for teams that run more than one website.
Review queue
Write once, route anywhere
A post is one row with a list of sites on it. Tick the sites it belongs on, or leave it on the defaults and it goes where the source is routed. Fix a typo once and it is fixed everywhere.
Adding the fourth site is a row in a settings screen. No repository to fork, no theme to port, no deploy.
Routing
Status
In reviewYour sites serve the content via API
The SDK is fetch, so it runs in Node, browsers, Cloudflare Workers and Deno with no build step. Or skip it and call the endpoint. Use the bearer token with a JSON response.
// the key names its site, so no site is in the url import { createClient } from '@coppercms/sdk' const copper = createClient({ apiKey: process.env.COPPER_KEY }) const { data, nextCursor } = await copper.listPosts({ limit: 20, tag: 'company' })
GET https://api.coppercms.com/v1/wire/posts?limit=20 authorization: Bearer ck_a41f….8f2c…c1a9.k3Rd… 200 OK content-type: application/json cache-control: private, max-age=60 vary: Authorization { "data": [ … ], "nextCursor": "eyJ…" }
A signed POST on publish, update and unpublish. The timestamp is signed with the body.
Images upload once and serve from cdn.coppercms.com.
Everything between the writer and the site
Copper CMS holds the content, the review, the routing and the delivery. What it deliberately does not hold is the front end.
The editor
A title and a document. The toolbar offers exactly what the sanitizer keeps, so nothing can be formatted and then vanish on save. Images upload straight to the CDN.
Review
Draft, in review, approved, scheduled, published. Enforced in the state machine rather than the screen, so the rule holds wherever the request comes from.
Scheduling
A timestamp on the post and a worker that picks it up. One post that cannot publish is skipped and reported, not allowed to wedge the batch.
Sites and routing
Register a site once and it becomes a destination. A post carries a list of them; a post that names none lands on the defaults for its source.
Sources
A partner pushes to a url, or Copper CMS polls a feed. Either way the content lands in the same queue as everything written in-house.
The field mapper
Reads a real payload and guesses which field is the title. Nested objects and arrays are handled. A payload it cannot read is kept for fixing.
Delivery API
One endpoint, one bearer key, keyset paging. The key names its site at issue time, so the url does not have to.
Outbound webhooks
A signed POST on publish, update and unpublish, with retries and a delivery log. The signature covers the timestamp as well as the body.
Team and roles
Owner, editor and writer, added by an invite link. Access is account-wide, so everyone on an account works on every site in it.
Start with one site
Add the second when you need it. The free plan does not ask for a card.
