http://localhost:3000; a production key only authenticates against
https://api.craftkit.dev.
One key, full project access
A project key is full-access within its project. There is no per-key scope, role, or permission column today — every validck_live_ key for a project can do all of the following
for that project, with no per-org provisioning:
Render
POST /v1/templates/:slug/renderRead templates
GET /v1/templates and GET /v1/templates/:slugPoll renders
GET /v1/renders/:idCreate shares
POST /v1/renders/:id/sharesGET /v1/embed/builder/templates (list) and
GET /v1/embed/renders/:id/download (private stream).
For a headless render integration you need exactly one
ck_live_ key, and the template
must live in the project that key belongs to. No admin key, no embed session, no provisioning.401 vs 404
These two are easy to confuse when a render “fails”:- 401
unauthorized— the key is missing, malformed, revoked, or from the wrong environment. - 404
template_not_found— the key authenticated fine, but no template with that slug exists in that key’s project. Usually means the key resolves to a different project than you expect, or the template was never created/published there.
The admin key is separate
CRAFTKIT_ADMIN_KEY is a single global environment secret that gates only the multi-tenant
provisioning endpoints (POST/PATCH /v1/admin/provision). It is compared directly against the
env value and never consults the API-key table — so a ck_live_ key sent to /v1/admin/provision
returns 401. You do not need the admin key for headless rendering.
Key scope
Today a project key is all-or-nothing within its project, which means the key you embed in a backend service can also create and publish templates. There is no render-only or read-only scoped key yet (roadmap). Until then:- Mint a dedicated key per integration (rendering, embed, inbound webhooks each separate) so you can revoke one without disrupting the others.
- Store keys in environment variables, never in client code or logs.