Personal access tokens
Bearer tokens for scripts, CI and AI agents — format, creation, listing and revocation, scoping through profiles, and usage metering.
A personal access token (PAT) is a bearer credential that authenticates as its owner. It is not a separate identity: a request made with a PAT has exactly the owner’s profile, groups and shares. This is what the CLI, MCP clients and integrations use.
Format#
klt_ followed by 40 random characters. The full value is shown once at creation; the server stores only a
SHA-256 hash and the first characters (tokenPrefix) for recognition.
Create#
Self-service (the app: Profile → API tokens; CLI: kelta token create --name ci --expires-in 90):
POST /api/me/tokens
{ "name": "ci", "expiresInDays": 90 }
expiresInDays is 1–365 (default 90). A user may hold at most 10 active tokens; expired tokens do not count.
Administrators can mint a token for another user — typically a service user — with MANAGE_USERS:
POST /api/admin/users/{userId}/tokens
{ "name": "catalog-feed", "expiresInDays": 365 }
(Setup → Administration → Users → Mint token; kelta users token-create.) This is audited as
PAT_ADMIN_CREATED, distinct from self-service PAT_CREATED.
Platform administrators (MANAGE_TENANTS) can mint a bootstrap token inside another tenant, as that tenant’s
seeded System Administrator, so automation can configure a tenant nobody has claimed yet:
POST /api/tenants/{tenantId}/bootstrap-token
{ "expiresIn": "1h" }
Bootstrap tokens last 1 hour by default and 24 hours at most (30m, 2h or ISO-8601 PT2H). They work only in
the tenant they were minted in, the platform tenant cannot be targeted, and each mint is audited as
TENANT_BOOTSTRAP_TOKEN_ISSUED. See Tenants → Bootstrap token.
Use#
Authorization: Bearer klt_...
Tenant resolution works as for any request: the /<tenant>/api/... path prefix, an X-Tenant-Slug /
X-Tenant-ID header, or a custom domain. See API authentication.
A token is valid only in the tenant it was issued in. On any other tenant’s URL the gateway answers 401.
List and revoke#
GET /api/me/tokens # id, name, tokenPrefix, scopes, expiresAt, lastUsedAt, createdAt, requestCount
DELETE /api/me/tokens/{id} # immediate — the hash is added to a revocation set every gateway consults
kelta token list|revoke, kelta auth logout --revoke.
Administrators with MANAGE_USERS can list and revoke another user’s tokens in their tenant:
GET /api/admin/users/{userId}/tokens # live tokens: id, name, tokenPrefix, scopes, createdAt, expiresAt
DELETE /api/admin/users/{userId}/tokens/{tokenId} # audited as PAT_ADMIN_REVOKED
A user outside the tenant, or a token that user does not hold, answers 404. Deactivating a user (status leaves
ACTIVE) or deleting one revokes all of their tokens.
Scoping a token: profiles, not scopes#
The scopes attribute is informational. What a PAT can do is what its owner’s profile allows. To build a
narrowly scoped integration token, create a service user with a purpose-built profile:
- Profile with
API_ACCESSand nothing else. - Object permission
canReadon the one collection (no create/edit/delete). - Service user on that profile; mint a PAT for it as an administrator.
Reads succeed; every write and every other collection answers 403. The full recipe with a verified transcript is
in PAT-based read-only API access.
Usage metering#
Every authenticated request with a PAT — allowed or refused — increments its lifetime requestCount, readable by
the owner on GET /api/me/tokens. This is independent of the tenant’s daily apiCallsPerDay governor budget,
which every request also counts against.
Caveats#
lastUsedAtis not currently maintained; rely onrequestCount.- Revocation on deactivation is best effort: if Redis is unreachable at that moment the token row is still marked
revoked, but a gateway that already cached the token can keep honouring it until its cache entry expires. Remove
API_ACCESSfrom the profile for a guaranteed immediate cut-off. - MCP endpoints accept only PATs; JWTs are rejected there.