Manage API keys
Mint, list, and revoke personal API keys for non-interactive callers (CI scripts, automation, internal tools).
API keys are user-scoped — a key inherits your org memberships, roles, and permissions. They don't carry their own scope. To limit blast radius, use a dedicated user with only the roles you want, and mint the key under that user.
For the conceptual model (what a key actually authenticates,
why no pk_ prefix, how 2FA interacts with keys), see
Reference: API keys.
Where they live
Sidebar → Settings → API Keys card. Lists your own keys only — there's no admin "see other users' keys" view.
Create a key
New API key button → drawer:
- Name — what the key is for (e.g.
github-actions-deploy,prometheus-remote-write) - Expires in days — default 90. The server clamps to
APIKeyMaxExpiryDaysif you ask for more (silent — it just caps your input)
Submit. The result page shows the 64-character hex plaintext exactly once, with a copy-to-clipboard button. Save it now:
- Paste into your password manager / CI secret store
- For GitHub Actions: add as a repo or org secret (e.g.
DTEDGE_API_KEY) - For local scripts: drop in your
~/.config/<tool>/keyringor similar
There is no recovery. If you close the page without copying, revoke the row and mint a new one.
Use the key in HTTP
Send it as the X-API-Key header:
curl -H "X-API-Key: <your-64-char-hex>" \
https://your-host/api/orgs/<org_id>/helm/list
Both auth headers (Authorization: Bearer <jwt> and
X-API-Key: <hex>) are accepted. If you somehow send both, JWT
takes precedence.
For SDKs that take a generic "auth header", configure them to
send X-API-Key rather than Authorization.
List, search, identify your keys
The API Keys table shows:
- Name — what you typed at create
- Expires at — absolute timestamp; never blank
- Last used — most recent request timestamp; blank if never used
- Actions — Revoke
The plaintext key itself is never shown again — only the row metadata. If you've lost the plaintext, treat it as gone and revoke the row.
Revoke a key
Row actions → Revoke → confirm. The row disappears.
Subsequent requests with that key return 401 invalid api key.
Revocation is immediate — there's no propagation lag, no revocation list to wait for. Caches are not at play; the server checks the SHA-256 hash on every request.
Rotate a key
The rotation pattern is deliberate, not automatic:
- Mint a new key with the same name + a date suffix (e.g.
github-actions-deploy-2026-04) - Update the consumer to use the new key
- Verify — let the consumer make at least one successful request; check the new row's "Last used" updates
- Revoke the old row
Don't skip step 3. If the new key is broken (typo'd in CI, wrong permissions), you'll find out before you cut over.
When to mint a fresh key vs reuse
- One key per consumer. Don't share a key between two scripts. If one leaks, you can't revoke without breaking the other.
- Rotate before expiry, not on it. If your default is 90 days, schedule rotation at day 60. A key that expires mid-deploy is a worse outage than 5 minutes of pre-emptive rotation.
- Mint for short-lived scripts too. Per-task key + revoke when done is a cheap habit that keeps your active key list tidy.
What an API key won't do
- Bypass casbin. Same permissions as your user
- Authenticate webhooks from external systems — those use HMAC signatures, not API keys
- Bypass 2FA at the policy level. The key skips the TOTP prompt (no second factor at use time) but a server policy of "all users must enrol in 2FA" still applies to your interactive logins
- Bypass the license gate.
LICENSE_MISSING/LICENSE_GRACE_READONLYapply to API key requests too - Carry an org or role of its own. Use a dedicated user if you want a narrower scope
Common errors
invalid api key— wrong key, key revoked, key not yet active. Check the consumer's secret; verify the row still exists in the UIapi key expired—expires_atis past. Mint a new one, update the consumerUSER_DEACTIVATED— your user'sis_active=false. Ask admin to reactivatepermission denied— your user doesn't have the casbin permission for what the script is trying to do. Either give the user a wider role or use an account that already has it
See also
- Reference: API keys
- How-to: set up 2FA
- Reference: error messages
- Backend docs: Concept → API Keys