Quickstart

Two credentials, doing opposite jobs. A delivery key reads content out. A webhook url lets a partner push content in. Both are shown once.

v1Stable

1. Issue a delivery key

On the Delivery keys screen, pick the site the key should read. A key is bound to that site when it is issued, which is why no site appears in the url.

ck_a41f9c2e77b1.8f2c4e1d9a03b7c6e5f4a2d1c0b9a8e7.k3Rd…

2. Read posts

import { createClient } from '@coppercms/sdk'

const copper = createClient({ apiKey: process.env.COPPER_KEY })

const { data, nextCursor } = await copper.listPosts({ limit: 20 })
const post = await copper.getPost('a-new-office-in-lisbon')

3. Or call it directly

GET https://api.coppercms.com/v1/wire/posts?limit=20
authorization: Bearer ck_a41f…

200 OK
cache-control: private, max-age=60

4. Take a feed from a partner

Create a source and Copper CMS issues one url. Hand it over as it is. There is nothing else for the partner to configure.

POST https://api.coppercms.com/v1/hook/8f2c…c1a9.k3Rd…
content-type: application/json

{ "title": "…", "body": "…" }

Responses

CodeMeaning
200The request succeeded. Listing and single-post reads both answer 200, and so does the ingest webhook, on a new article and on a re-send alike.
400The field map produced no title, the body was not valid JSON, or a cursor was not one this endpoint issued.
401The credential is unknown, revoked, tampered with or malformed. All four answer the same way.
402The account is over a plan limit, or it is suspended.
403The credential is real, but the source is not active or the key does not reach that site.
404No post with that slug is published to the site the key names.
413The payload is larger than the ingest webhook accepts.

Verifying an outbound webhook

The signature covers the timestamp and the raw body joined by a dot. Signing the body alone would leave the replay window decorative.

const signed = `${timestamp}.${rawBody}`
const expected = createHmac('sha256', secret).update(signed).digest('hex')

// compare in constant time, and reject a timestamp
// more than five minutes old