Authentication
Authenticate every request with a project API key.
All requests to /v1/* carry a project API key in the Authorization header. Keys are scoped to one project and can be created or revoked from the dashboard. This page covers the format, how to create and rotate keys, scope, and the errors you'll see when something is off.
Quick Start
Three languages, the same authenticated request.
curl
curl https://api.craftkit.dev/v1/renders/0193c2c3 \
-H "Authorization: Bearer $CRAFTKIT_API_KEY"Node.js
const res = await fetch('https://api.craftkit.dev/v1/renders/0193c2c3', {
headers: {
Authorization: `Bearer ${process.env.CRAFTKIT_API_KEY}`,
},
});Python
import os, requests
res = requests.get(
"https://api.craftkit.dev/v1/renders/0193c2c3",
headers={"Authorization": f"Bearer {os.environ['CRAFTKIT_API_KEY']}"},
)Header format
Authorization: Bearer ck_live_<random>| Prefix | Environment |
|---|---|
ck_live_ |
Production renders, billable, hits the live worker pool |
ck_test_ |
Test renders, free, watermarked output |
Creating a key
Dashboard → Project → API keys → enter a name → Create key. The cleartext key is returned once — copy it immediately. We only store the SHA-256 hash, so a lost key cannot be recovered.
Each project can have many keys, each independently revocable. Use one key per integration so you can rotate without coordinating an outage across services.
Revoking a key
Same screen → Revoke. Subsequent calls return 401 invalid_credentials immediately (no grace window). Issue a replacement first, deploy it, then revoke.
Scope
API keys are scoped to one project. They can:
- POST a render against any template in that project.
- Read renders for that project.
- Trigger inbound webhooks for templates in that project (the inbound token authenticates instead, but the key still owns the resulting render row).
They cannot:
- Modify templates, projects, or webhook configs (use the dashboard).
- Read other projects' data.
- Mint embed sessions for partner integrations (use the embed publishable + signing key pair instead).
Errors
| HTTP | Code | When | Fix |
|---|---|---|---|
| 401 | missing_authorization |
No Authorization: Bearer ... header |
Add the header |
| 401 | invalid_credentials |
Key is unknown or revoked | Mint a new key |
| 403 | partner_suspended |
Project's embed partnership is suspended | Contact support |
Best practices
- Never embed keys in client code. Browser bundles, mobile apps, and public repositories all leak. Keep keys on a server.
- Use environment-specific keys. Separate
ck_test_for staging andck_live_for production. Never share a key across environments. - Rotate quarterly. Mint a new key, deploy it, revoke the old one once metrics confirm zero traffic on the previous key.
- One key per workload. Background jobs, the synchronous API path, and the inbound webhook handler each get their own key. Revoking one doesn't disrupt the others.
Troubleshooting invalid_credentials
The most common cause: the key was created in a different environment.
API keys are stored as SHA-256 hashes in the database for the environment where they were created. A key you minted in your local dev environment does not exist in production, and vice versa. If you see invalid_credentials after deploying, the fix is to mint a new key inside the target environment's dashboard.
Local dev key → only works against http://localhost:3000
Production key → only works against https://api.craftkit.devSecond most common cause (embed sessions): embed is not enabled for the project.
POST /v1/embed/sessions and POST /v1/embed/catalogs require the API key's project to have embed enabled. An otherwise valid key returns invalid_credentials on these endpoints if the project has no embed partner record.
Fix: Dashboard → Project → Embed → Overview → Enable embed mode.
Checklist
| Symptom | Check |
|---|---|
| Valid key works locally, fails in production | Re-mint the key in the production dashboard |
Key works on /v1/renders/* but not /v1/embed/* |
Enable embed mode for the project |
| Key was working, now suddenly fails | Check if it was revoked (Revoke column in API keys tab) |
missing_authorization instead of invalid_credentials |
The Authorization: Bearer ... header is missing entirely |
Related
- POST /v1/templates/:slug/render — the most common authenticated call
- Errors — the full envelope shape
- Inbound webhook — alternative auth via per-template token