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:

  1. An org_id path parameter, where the endpoint's URL has one.
  2. An X-Org-Id header carrying the organization's upzero id (what the CLI sends after up0 config profile add picks a default organization).
  3. An X-Org-Slug header 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:

ScopeReaches
monitors:readListing and reading monitors and their checks
monitors:writeCreating, updating, pausing, resuming and deleting monitors
organization:readReading the organization and its member list
organization:writeUpdating the organization
heartbeats:writePosting 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.

On this page