Docs
Dashboard

Getting started

API keys

Two kinds of key, what each one may do, and why the browser gets its own.

Every request carries a key, and the key selects the project:

Authorization: Bearer sk_…

There is no project id in any URL. The same path serves different content for different keys, which means you can point a staging deploy at a different project by changing one environment variable.

The two kinds

sk_… secretpk_… publishable
Where it runsServer-side onlySafe in a browser
Origin restrictionNoneLocked to the origins you list
Can list releasesYesNo
Can writeYesNo, except a knowledge base vote
Can expireYes, if you chooseNo
Recoverable after creationNoYes

Secret keys

A secret key is shown once, when you create it. We store a hash, not the key, so there is no “reveal” button and support cannot recover it. Losing one means creating another and revoking the old.

Treat it like a database password. It reads everything the project serves, including unpublished releases, and it can write.

Publishable keys

A publishable key is designed to be readable. It ships in page source, in a mobile bundle, in a view-source. That is the point, and it is why the key alone is not trusted as a claim about who is calling.

Two things bound it:

  1. An origin allow-list. Add your site’s origins to the key and a browser request from anywhere else is refused. www. is ignored when matching, so listing https://example.com covers https://www.example.com.
  2. A tighter rate budget. Publishable keys are counted per key and per address, so one visitor hammering your help centre is limited without limiting every other visitor holding the same key.

Two purposes: service and session

Every key is one of two things, chosen when you create it, and it decides whether the key expires.

Service. A running server, a site, a build. Never expires. This is the key your production frontend holds, and it is the default, because the failure mode of the other choice is a live site going dark at whatever hour a clock runs out, on a day nobody deployed anything.

Session. A terminal, a script, an AI agent. Stops working after 7 days by default, and 90 days at most.

Why session keys exist

A key pasted into a terminal, or handed to an agent to do an afternoon’s work, outlives the work by however long the machine it landed on survives. Nobody remembers to revoke it, because nobody remembers it exists.

Seven days covers the work and closes the rest. If you find yourself wanting a session key that lasts a year, what you want is a service key.

Knowing before it lapses

An expiring key is a good security property and a bad surprise, so it is announced three ways:

  • X-Key-Expires-At on every response, an ISO timestamp.
  • GET /v1/ping and GET /v1/introspection carry a key.expiry object with days_remaining and expiring_soon.
  • Once it lapses, requests fail with key_expired, naming the date, rather than a generic 401:
{
  "error": {
    "type": "authentication_error",
    "code": "key_expired",
    "message": "This API key expired on 2026-08-30T05:40:00.642Z and no longer works. Session keys are short-lived on purpose… Create a new key in your ZevContent dashboard; if this key belongs to a running service rather than a script, create it as a SERVICE key, which does not expire."
  }
}

That distinction matters most for agents: told only “invalid key”, an agent concludes it has the wrong value and mints a replacement, instead of telling the person their key aged out.

Check a key

GET /v1/ping
curl https://api.zevcontent.net/v1/ping \
  -H "Authorization: Bearer $ZEVCONTENT_KEY"

Confirms the key resolves and names the project it belongs to. This is the first call to make when something is not behaving. It separates “wrong key” from “wrong request”, which are the two problems that look identical from a 404.

Rotating, revoking and expiring

Revoking is immediate: the next request with that key is refused. There is no grace period, so create the replacement, deploy it, and revoke afterwards.

Because a key belongs to one project, rotating one never affects another.

A key that has expired or been revoked stays visible in the dashboard for 30 days and is then deleted. Short-lived keys mean the list grows a tombstone every week, and a key list whose first page is dead keys stops answering the only question it exists for: what can currently reach my content? So the dashboard shows active keys by default and puts the rest behind a count you can click.

Which key for which job

  • A static site build. Secret, service. It runs on a build machine, not a browser, and it can ask for media_urls=permanent to refuse expiring image URLs.
  • A React or Vue frontend fetching at runtime. Publishable, with your origins listed.
  • A help centre with helpful/not-helpful voting. Publishable. The vote endpoint is the one write a browser should ever make, and it explicitly expects a publishable key.
  • A migration script or an AI agent creating content. Secret, and a session key. This is exactly the case session keys exist for: the work is finite, the key should not outlive it.

Updated at, Sunday, September 20, 2026