Guide
Authentication
How API keys, scopes, and rotation work.
Every Clement API request carries a bearer token in the Authorization header:
Authorization: Bearer cl_live_a1b2c3d4e5f6...
Live and test keys
Keys come in two flavors:
cl_live_…— production. Sends real email through real inboxes.cl_test_…— sandbox. Same API surface; sends never reach a real recipient. Detailed in Test mode.
The SDK warns if a single process mixes test and live keys — common cause of “I sent prod email from a staging script.”
Scopes
Each key has one of three scope tiers:
read—GETonly. Useful for dashboards and analytics jobs that shouldn’t be able to send or mutate.write— read + non-destructive POST/PATCH/PUT. Create contacts, send, import, manage sequences.admin— write + destructive ops: delete inboxes, rotate keys, manage webhooks, change billing.
Pick the smallest scope that does the job. If your background worker only needs to create sends and read events, give it a write key. If a script rotates webhooks, that’s an admin job — and probably wants auto_confirm: true so it doesn’t have to ack every confirmation prompt.
Creating and rotating keys
curl -X POST "https://api.clement.so/v1/api_keys" \
-H "Authorization: Bearer $CLEMENT_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"name": "Production worker",
"scope": "write",
"mode": "live"
}'
The response includes a secret field — this is the only time you’ll see it. Store it in your secrets manager immediately. The dashboard and GET /v1/api_keys only ever return the last four characters. Clement doesn’t store the secret with an Idempotency-Key either: retrying the create with the same key returns 409 idempotency_replay_unavailable (naming the key that was created) instead of a second key.
An API key only manages keys of its own mode. mode defaults to the calling key’s mode, and a cl_test_… admin key can’t create, list, rotate or revoke cl_live_… keys (creating one returns 403). Mint live keys from the dashboard or with a live admin key.
To rotate, POST /v1/api_keys/{id}/rotate. The new secret is returned, the old one is invalidated within 60 seconds. Plan a brief overlap window so in-flight requests don’t fail mid-flight.
import { Clement } from "@clement/sdk";
const c = new Clement({ apiKey: process.env.CLEMENT_API_KEY! });
const rotated = await c.apiKeys.rotate("key_01HZ8X3J9K7P2QV6R5N4M3T2L1");
console.log(rotated.secret);
Revocation
DELETE /v1/api_keys/{id} revokes immediately (well, within 60s — cached lookups expire). Lost a key? Revoke and rotate; do not try to recover the secret.
This is a destructive operation, so it requires ?confirm=true unless the key has auto_confirm enabled.
Dashboard sessions
The dashboard uses Better-Auth session cookies, not API keys. Both resolve through the same middleware to (workspace_id, scope). There is no “internal” API — anything the dashboard can do, an API key can do.
The TypeScript SDK supports both:
// Server-to-server
new Clement({ apiKey: process.env.CLEMENT_API_KEY! });
// Browser (the dashboard's own usage)
new Clement({ session: "cookie" });