Skip to main content
The Credentials page (/admin/credentials) manages the API keys the app calls out to. Keys saved here take effect immediately — no Vercel environment variable to set, no redeploy.

The providers

Each row on the Credentials page shows a real logo (via logo.dev), the provider’s purpose, and a status:
  • db-managed — an active key is stored, encrypted, in the database.
  • .env — no database key, but the corresponding environment variable is set on the server (e.g. HARVEST_API_KEY). Still usable; just not rotatable from this page yet.
  • not configured — neither. Adapters that need this provider will fail clearly rather than silently degrading.

Adding or rotating a key

Click Add key, paste the key, and save. Rotating a key doesn’t overwrite the old one — the previous key is deactivated (is_active = false, retired_at set) and a new row is inserted, so there’s an audit trail of every key that was ever active. Deactivate requires a confirm click (the button becomes Confirm / Cancel) before it takes effect — no accidental one-click removal of a working key.

How this stays secure

  • Keys are encrypted with AES-256-GCM before they ever touch the database (lib/crypto.ts), using a server-only CREDENTIAL_ENCRYPTION_KEY.
  • The browser never receives a key back in plaintext — only a masked hint like px_test_••••2345, generated at save time and never re-derivable from the stored ciphertext.
  • Decryption happens exclusively in server-side code (lib/domain/credentials.getProviderApiKey), which resolves a database-stored key first and only falls back to the environment variable if nothing is active in the database.
  • Every credential is scoped to your organization — the same encrypted key isn’t shared across organizations, and the lookup always goes through your current session.

Which endpoints actually consume a saved key today

As of this writing, only Anthropic is wired into a real code path (lib/providers/anthropic.ts, used by the calibrate screen’s extraction call). Harvest and Serper keys save and encrypt correctly, but nothing in the app calls those APIs yet — their provider adapters land in a later phase. Once they do, they’ll automatically pick up whatever’s saved here with no additional wiring.