The token
Every endpoint under/public/v1 takes an OAuth 2.0 access token in an Authorization header. The token must be minted for this audience:
v1 cannot be replayed against a future v2, and the two can run side by side with different permissions. A token minted for the developer portal’s audience is refused here, and vice versa.
A request with no usable token gets a bodiless 401 — before its body is read and before its parameters are validated, so an anonymous caller learns nothing about what an endpoint accepts.
What a token is allowed to do
Authorization has two independent planes, and a Team role grants nothing inside an Environment.1
Team membership
The caller must belong to the Team that owns the Environment in the URL. A token for another Team gets a
404 — not a 403 — because telling a stranger that something exists is itself a disclosure.2
An Environment grant
The caller must hold a grant on that Environment, at or above the level the endpoint needs. A Team Admin holding no grant is refused. That is the point of the model, not an edge case.
Every response that carries an Environment tells you which of the three you hold, in its
role field — so a client can tell whether a write will be refused before attempting it.
GET /environments lists only the Environments you were granted. It is not a listing of the Team’s Environments with yours marked.
Which level each endpoint needs
Integrations are
admin because they decide which files the publish pipeline builds for every published version and every Variant of the Theme — that is administration of what the Environment serves, rather than authoring inside it.
See Roles and permissions for the full model.
The serving edge authenticates differently
cdn.livry.dev takes no bearer token at all. A Theme is either served publicly or served signed, per Theme. See Signed URLs.
Never send a Public API token to the serving edge, and never put one in a browser. It is an administrative credential for your backend and your build pipeline.