Skip to main content

Overview

The API supports three ways to authenticate: a bearer token, an API key, and a short-lived acting token minted for a service account. All three are presented as a single request header — nothing else about a request changes based on which one you use. The examples below use /users/me — see Conventions for details on the me alias and other shared API patterns.

Bearer Token Authentication

In request headers:
A bearer token comes from signing in — see Google Sign-In and Anonymous Sign-In, refreshed via Refresh Token — and authenticates as the signed-in user directly, with that user’s full standing on everything they can reach.

API Key Authentication

In request headers:
An API key is minted for a target — an Organization, a Blob, or a User — and carries a role (read, write, or admin) fixed at creation. A key minted at target=user/{account} authenticates as that account: this is the only way to act as a service account. See Create API Key for how to mint one.
An API key never grants more than the role it was minted with, even when the user behind it holds a higher-role membership on the target being accessed. See Role Cap below.

Acting Token Authentication

In request headers:
Minted by Impersonate, an acting token authenticates as a service account on behalf of the human who minted it — presented exactly like a bearer token, not a separate header. It lasts 60 minutes, is single-shot (minting one issues no refresh token), and cannot be revoked early: it is a stateless JWT, so the 60-minute expiry is the only control there is. See Impersonate for who can mint one. Because it carries the acting human, it is admitted where a service account’s own key is refused, with one exception — an org- or blob-scoped API key is not minted while acting, since it would record the impersonated account rather than the human (400 cannot_mint_while_acting; target=user is admitted, and is what acting mode is for). See The Access Perimeter below.

The Access Perimeter

One question separates the three credentials above: is there a human behind this request? A bearer token is the human. An acting token names the human who minted it. An API key answers with whoever its user_id resolves to — a human for the ordinary org, blob and personal keys, and nobody at all when that user_id is a service account. That distinction decides one thing, across ten operations. A service account authenticated by its own key may do product work — read and write Blobs, commit Revisions, run workflows, post into threads — at whatever role its memberships give it. It may not change who can access what. Every operation that would receives 403 with error: forbidden, regardless of the role the key was minted with, including admin: Reads are never governed — listing members, keys or credentials is ordinary work. The two PATCH endpoints are governed only on the visibility field, and only when it actually changes; every other field, and a visibility that matches what is already stored, goes through untouched. Human callers are unaffected throughout, whichever of the three credentials they use. Where automation genuinely has to cross the perimeter, mint an acting token — that is what it is for. Full model: Service Accounts → The Access Perimeter.

Role Cap

An API key’s effective role on a target is the lesser of two things: the role it was minted with, and the role its user holds there through membership or ownership. Holding an admin membership on an Organization does not let a read-scoped key act as admin there — the key caps the role no matter how much standing its user separately has. A bearer token and an acting token are unaffected: both carry the user’s full standing directly, with no minted credential in between to cap.
This closes a real gap: before this change, the membership role won, so a narrowly-scoped key could reach further than intended by riding its user’s broader membership. Measured against production before the fix shipped, no existing key was affected.