Skip to main content

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 APIKeyMaxExpiryDays if 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>/keyring or 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:

  1. Mint a new key with the same name + a date suffix (e.g. github-actions-deploy-2026-04)
  2. Update the consumer to use the new key
  3. Verify — let the consumer make at least one successful request; check the new row's "Last used" updates
  4. 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_READONLY apply 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 UI
  • api key expired — expires_at is past. Mint a new one, update the consumer
  • USER_DEACTIVATED — your user's is_active=false. Ask admin to reactivate
  • permission 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​