Authentication
Bearer tokens for people and machines, and how a request's organization is chosen.
Every request carries a Zitadel-issued JWT in the Authorization header.
Authorization: Bearer <token>There is no separate "API key" login flow for humans. The token is the same one the console and CLI use after signing in through Zitadel.
Human callers
A person authenticates by signing in through Zitadel (the console does this
with a server-side OIDC flow; the CLI opens a browser for the same login and
stores the resulting token). The access token's sub claim identifies the
user, and the backend resolves their upzero profile from it.
Machine callers
A script or service authenticates the same way, with a Zitadel machine user's
token (a service-account personal access token) in the same Authorization
header. A machine token has no email claim, which is how the backend tells
it apart from a human token: a caller with no email is looked up as a service
account by its Zitadel subject id, never treated as a human user. A token
that matches neither a human nor a known, unrevoked service account is
rejected with 401.
Create a service account from the console's organization settings. It is issued its own token the same way a human's session is, and that token is used exactly like a human's for every endpoint that accepts one.
Choosing the organization
Every org-scoped endpoint needs to know which organization the request is
for. The primary source is the token itself: a Zitadel session for a
specific organization carries that organization's id as an
urn:zitadel:iam:org:id claim, and the backend resolves it from there. No
separate parameter is needed for a normal, per-organization login.
A token issued without an organization claim (a global login, before an organization is chosen) falls back to, in order:
- An
org_idpath parameter, where the endpoint's URL has one. - An
X-Org-Idheader carrying the organization's upzero id (what the CLI sends afterup0 config profile addpicks a default organization). - An
X-Org-Slugheader or cookie carrying the organization's slug (what the console sends).
A request that matches none of these is rejected with 403 and error code
NO_ORGANIZATION.
Scoped API keys
A narrower alternative to a full bearer token exists for a handful of
endpoints: an API key (X-API-Key header, prefixed up0_live_), created
from the console and bound to one organization and a set of scopes. A
request that sends X-API-Key is authenticated by the key instead of the
Authorization header, and can only ever reach that key's own organization.
Only endpoints backed by an organization-scoped repository accept a key today:
| Scope | Reaches |
|---|---|
monitors:read | Listing and reading monitors and their checks |
monitors:write | Creating, updating, pausing, resuming and deleting monitors |
organization:read | Reading the organization and its member list |
organization:write | Updating the organization |
heartbeats:write | Posting to a heartbeat monitor's push URL |
Incidents, workflows and status page management are not reachable with an API key. Their data is scoped by the acting user, not by organization, so there is no user for a key (which carries no identity of its own) to act as. This is a real limit of what exists today, not an oversight left out of a table.
Errors
See Errors for the response envelope. A request with no
Authorization header and no X-API-Key is rejected with 403 (FastAPI's
own "not authenticated" response, distinct from a token that was presented
and rejected). A token that fails verification, is expired, or belongs to a
revoked session is rejected with 401.