Mint an API key
const url = 'https://example.com/v1/tokens';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"for":{"kind":"user","id":"example"},"role":"admin","capabilities":["example"],"label":"example","expiresAt":"2026-04-15T12:00:00Z","projectId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/tokens \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "for": { "kind": "user", "id": "example" }, "role": "admin", "capabilities": [ "example" ], "label": "example", "expiresAt": "2026-04-15T12:00:00Z", "projectId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" }'An API key acts for one principal (for: a person or a service account; default the caller), with that principal’s grants. Its role is a ceiling under them and its projectId a limit. Returns the plaintext token exactly once. A person or a service account’s key mints its own keys; only a tenant admin mints for someone else, or an admin key. A caller can only grant capabilities it holds, and a key limited to a project mints only keys limited to it. A key that expires mints only keys that expire no later than it, and a session opened by token sign-in mints as the key it was opened with. Only mounted when the deployment supplies a TokenAdmin.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”Caller-supplied idempotency key, scoped to the caller: the same key from someone else in the tenant is their own request (from 0.1.5). Retries with the same key return the original response byte-identical (per docs/API-ROUTE-CONVENTIONS.md §3.1). A retry sent while the first request still runs is answered 409 idempotency-key-in-flight with Retry-After, on a runtime whose store holds keys (from 0.1.5); retry after it, and you get the first request’s answer. An answer that carries a secret (a new API key or public run token, a session token, a generated signing secret) isn’t kept: a retry gets 409 idempotency-key-replay-withheld, with the first request’s status and when it succeeded (from 0.1.5).
Request Body
Section titled “Request Body”A new API key. for is whom it acts for: the caller by default; only a tenant admin mints for someone else.
object
Whom an API key acts for: a person, or a service account.
object
The user id, or the service account id.
The most the key may do, under its principal’s grants: an admin key may administer the tenant when its principal is a tenant admin; a member key takes no admin action on the tenant, whoever it’s for; below it, its principal’s roles hold (a project admin’s member key administers that project). Default member; admin needs a tenant admin minting it.
Framework capabilities the key carries (env:write, secrets:write, …). A caller can only grant capabilities it holds. Default none.
Optional human-readable label.
ISO 8601 timestamp. Absent: the key never expires. When the minting key expires (or the key a token sign-in session was opened with), it’s required and no later than that key’s (token-expiry-exceeds-minter).
Limit the key to this project: a request naming another project is refused (key-project-mismatch). A key limited to a project mints only keys limited to it.
Responses
Section titled “Responses”Token minted.
The new key, plus its secret.
object
Whom an API key acts for: a person, or a service account.
object
The user id, or the service account id.
The most the key may do, under its principal’s grants: an admin key may administer the tenant when its principal is a tenant admin; a member key takes no admin action on the tenant, whoever it’s for; below it, its principal’s roles hold (a project admin’s member key administers that project).
Framework capabilities the key carries (env:write, secrets:write, …). A caller can only grant capabilities it holds.
The project the key is limited to.
Who minted it: user:<id> or service_account:<id>.
Set once revoked; a revoked key never authenticates again.
When the key last authenticated a request (updated at most once a minute).
Plaintext bearer token. Returned exactly once at mint time.
Example
{ "principal": { "kind": "user" }, "role": "admin"}Malformed request body, or the body’s projectId isn’t a project id (a UUID).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Missing / malformed / expired / revoked bearer token.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Not a tenant admin where one is needed, or a capability the caller does not hold (permission-denied); an admin key for a principal that isn’t a tenant admin (role-exceeds-principal); a key limited to a project minting for another (key-project-mismatch); a key that would outlive the key minting it, with an expiresAt later than that key’s or none (token-expiry-exceeds-minter, its details.latestExpiresAt the latest allowed).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The body’s projectId names no project of this tenant (project-not-found), or for names no person or service account (principal-not-found).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Idempotency-Key reused with a different body (idempotency-key-body-mismatch), or a retry of a request that succeeded: its answer carried a secret, which isn’t kept (idempotency-key-replay-withheld, with its status and at).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The request body is over the API’s limit (request-too-large; details.maxBytes says how much).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Server error (unmapped domain code or framework crash).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}See also
GET /v1/tokens: List API keys- Error code
idempotency-key-body-mismatch - Error code
idempotency-key-replay-withheld - Error code
key-project-mismatch - Error code
permission-denied - Error code
principal-not-found - Error code
project-not-found - Error code
request-too-large - Error code
role-exceeds-principal - Error code
token-expiry-exceeds-minter - Every error code