Getting started
QuickstartConceptsRecommended setupsAuthenticationAPIs
Delivery APIOutbound webhooksLogsIngest webhookImagesErrorsLibraries
TypeScript SDKReference
Plans and limitsTypeScript SDK
A client for the delivery API.
npm install @coppercms/sdk
The SDK uses fetch and nothing else. It runs in Node and browsers and Cloudflare Workers and Deno. It has no dependencies and no build step.
Keep the key on a server. A key in a browser bundle is public.
Create a client
import { createClient } from '@coppercms/sdk' const copper = createClient({ apiKey: process.env.COPPER_KEY })
| Option | What it is |
|---|---|
| apiKey | The delivery key. Required. |
| baseUrl | Defaults to https://api.coppercms.com. |
| fetch | A different fetch. For tests or a custom agent. |
listPosts
const { data, nextCursor } = await copper.listPosts({ tag: 'news', since: new Date('2026-08-01'), limit: 20, })
Takes tag and since and limit and cursor and q. It also takes an AbortSignal.
since takes a Date or a string.
getPost
const post = await copper.getPost('a-new-office-in-lisbon')
Returns null when the article is not found. Every other failure throws.
allPosts
for await (const post of copper.allPosts({ since })) { await upsert(post) }
Walks every page.
verifyWebhook
import { verifyWebhook } from '@coppercms/sdk' const ok = await verifyWebhook({ secret, rawBody, signature, timestamp })
Returns a promise. It uses the crypto built into the platform.
It never throws. A bad signature returns false. An unreadable timestamp returns false. The wrong secret returns false.
The timestamp is checked before the signature.
| Default tolerance | 5 minutes either way |
|---|
Change it with toleranceSeconds.
Errors
import { CopperError } from '@coppercms/sdk' try { await copper.listPosts() } catch (error) { if (error instanceof CopperError) handle(error.status) }
CopperError carries the HTTP status and the message from the API. See Errors.
