Docs
Dashboard

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

GET /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

StatuscodeWhat it means
401unauthenticatedMissing, malformed, unknown or revoked key. Revocation is immediate.
403origin_not_allowedA publishable key used from an origin not on its list.
403secret_key_requiredA write, or a release listing, attempted with a publishable key.
401key_expiredA 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