API reference
Authentication
Bearer keys, what each kind may do, and the failures that look like authentication and are not.
Authorization: Bearer sk_…
The key selects the project. No project id appears in any URL, so pointing a deploy at a different project is a change to one environment variable.
Key kinds
sk_…, secret. Server-side only. Reads everything the project serves,
can list every release, and can write. Shown once at creation; we store a hash,
so it cannot be recovered.
pk_…, publishable. Safe in a browser. Restricted to the origins listed
on it, cannot list releases, and cannot write, with one deliberate exception:
the knowledge base vote endpoint.
Full comparison in API keys.
Service and session keys
A secret key is created as one of two purposes, and it decides whether the key expires:
service: a running server, site or build. Never expires. The default.session: a terminal, script or AI agent. Expires in 7 days by default, 90 at most.
Every response carries X-Key-Expires-At when the key has one, and
GET /v1/ping and GET /v1/introspection report key.expiry with
days_remaining and expiring_soon. Once lapsed, requests fail with
key_expired naming the date, not a bare 401.
Publishable keys are always service: they ship inside a deployed bundle,
which cannot refresh a credential it was compiled with.
Verify a key
/v1/ping curl https://api.zevcontent.net/v1/ping \
-H "Authorization: Bearer $ZEVCONTENT_KEY"
Confirms the key resolves and names its project. Make this call first when something is not behaving. It separates “wrong key” from “wrong request”, which look identical from a 404.
Failures
| Status | code | What it means |
|---|---|---|
| 401 | unauthenticated | Missing, malformed, unknown or revoked key. Revocation is immediate. |
| 403 | origin_not_allowed | A publishable key used from an origin not on its list. |
| 403 | secret_key_required | A write, or a release listing, attempted with a publishable key. |
| 401 | key_expired | A session key past its expiry. The message names the date. |
The origin check
A publishable key with origins listed is refused from any other browser
origin. The check compares the Origin header, ignoring www., so
https://example.com covers https://www.example.com. Scheme and port are
part of an origin: http://localhost:3000 and http://localhost:5173 are not
the same.
It is best-effort by nature. curl sends whatever it likes, so an allow-list
does not make a publishable key secret. It stops your key powering somebody
else’s site, which is the realistic abuse.
What a key can see
A key sees its project’s published content, and nothing outside that project. There is no endpoint that crosses a project boundary, and an id from another project is not found rather than forbidden, because a “forbidden” would confirm the id exists.
Drafts are never served on /v1, whichever key you hold. A draft article
answers blog_article_not_published or kb_article_not_published, which says
what is wrong rather than pretending the slug does not exist.
Updated at, Sunday, September 20, 2026