API Keys
Create long-lived API keys (publicId.secret, Argon2id-hashed) for programmatic access via X-Api-Key or Bearer.
API keys are long-lived credentials for service-to-service calls, scripts, or background jobs. Unlike JWTs, they do not expire quickly unless you set expiresAt. Use them when a user is not interactively present (cron jobs, internal services, CI/CD calling this API).
The server stores only an Argon2id hash of the secret segment. The full key string is shown once at creation.
Key format
Each key is a single string with two segments separated by the first dot:
publicId— Starts withpr_, then a random URL-safe segment. Used for database lookup (not secret).secret— High-entropy random segment (base64url). Proves possession; never stored in plaintext.
Example shape (values are illustrative):
pr_<randomPublic>.<randomSecret>
Tenant and environment are not embedded in the string. They are bound to the key in the database and must be sent explicitly in headers (see below).
Lifecycle
- Create —
POST /auth/api-keyswith a JWT (not an API key). Body:name, optionalpermissions, optionalexpiresAt(ISO 8601). - Store — Response includes
apiKey(full string),id(UUID), andpublicId. SaveapiKeyin a secret manager; it cannot be retrieved again. - Use — Send the key in
X-Api-Keyor asAuthorization: Bearer <apiKey>(opaque token, not a JWT). Always sendX-Tenant-IdandX-Environment; they must match the key’s tenant and environment. - List —
GET /auth/api-keyswith a JWT returns metadata for keys in the current tenant/environment (id,publicId,name,permissions,expiresAt,revokedAt, etc.). Never returns the secret. - Revoke —
DELETE /auth/api-keys/:idwith a JWT setsrevokedAt. Revoked keys reject all API requests.
Create, list, and revoke are JWT-only so automation cannot mint or revoke keys without a user session (or equivalent).
Permissions
Default permissions are ['read', 'write']. You can pass a custom array (e.g. ['read'] for read-only).
For API key authentication, the API enforces:
- GET, HEAD, OPTIONS — require permission
read. - POST, PUT, PATCH, DELETE, and other methods — require permission
write.
JWT and Keycloak users are not restricted by this permission list on these routes (they use roles and other guards where applicable).
Expiry
expiresAt is optional. If set, the API rejects the key after that time with an authentication error.
Request parameters (create)
Parameters
Display name for the API key.
Permissions (e.g. read, write). Default: ['read', 'write'].
ISO 8601 expiry date. Optional.
Calling the API with a key
Prefer X-Api-Key so the key is not confused with a JWT:
X-Api-Key: pr_xxxxxxxx.<secret>
X-Tenant-Id: 123e4567-e89b-12d3-a456-426614174000
X-Environment: production
If both X-Api-Key and Authorization are present, X-Api-Key takes precedence for choosing the credential.
Swagger
The API docs register both Bearer and X-Api-Key. You can authorize with either.
Migrations and breaking changes
Database migrations that replace the legacy key storage invalidate previously issued keys. After upgrading, create new keys with POST /auth/api-keys.