/** * API Key Management Page (S-7.3) * * Dashboard page for managing API keys: * - Create keys (show full key once with copy button) * - List keys with prefix, name, environment, last_used_at, created_at * - Revoke keys with confirmation dialog * - Tier limit indicator */ import { getErrorMessage } from '@agentkitai/agentlens-core'; import React, { useState, useCallback, useEffect } from 'react'; import { useOrg } from './OrgContext'; import { listApiKeys, createApiKey, revokeApiKey, getApiKeyLimit, type CloudApiKey, type ApiKeyEnvironment, type ApiKeyLimitInfo, } from './api'; const ENVIRONMENTS: ApiKeyEnvironment[] = ['production', 'staging', 'development', 'test']; const ENV_LABELS: Record = { production: '🟢 Production', staging: '🟡 Staging', development: '🔵 Development', test: '⚪ Test', }; export function ApiKeyManagement(): React.ReactElement { const { currentOrg } = useOrg(); const [keys, setKeys] = useState([]); const [limitInfo, setLimitInfo] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Create form state const [showCreateForm, setShowCreateForm] = useState(false); const [newKeyName, setNewKeyName] = useState(''); const [newKeyEnv, setNewKeyEnv] = useState('production'); const [creating, setCreating] = useState(false); // Newly created key (shown once) const [createdKey, setCreatedKey] = useState(null); const [copied, setCopied] = useState(false); // Revoke confirmation const [revokeTarget, setRevokeTarget] = useState(null); const [revoking, setRevoking] = useState(false); const orgId = currentOrg?.id; const refresh = useCallback(async () => { if (!orgId) return; setLoading(true); setError(null); try { const [keyList, limit] = await Promise.all([ listApiKeys(orgId), getApiKeyLimit(orgId), ]); setKeys(keyList); setLimitInfo(limit); } catch (err: unknown) { setError(getErrorMessage(err) || 'Failed to load API keys'); } finally { setLoading(false); } }, [orgId]); useEffect(() => { refresh(); }, [refresh]); const handleCreate = useCallback(async () => { if (!orgId || !newKeyName.trim()) return; setCreating(true); setError(null); try { const result = await createApiKey(orgId, newKeyName.trim(), newKeyEnv); setCreatedKey(result.fullKey); setCopied(false); setShowCreateForm(false); setNewKeyName(''); setNewKeyEnv('production'); await refresh(); } catch (err: unknown) { setError(getErrorMessage(err) || 'Failed to create API key'); } finally { setCreating(false); } }, [orgId, newKeyName, newKeyEnv, refresh]); const handleRevoke = useCallback(async () => { if (!orgId || !revokeTarget) return; setRevoking(true); try { await revokeApiKey(orgId, revokeTarget.id); setRevokeTarget(null); await refresh(); } catch (err: unknown) { setError(getErrorMessage(err) || 'Failed to revoke API key'); } finally { setRevoking(false); } }, [orgId, revokeTarget, refresh]); const handleCopy = useCallback(async () => { if (!createdKey) return; try { await navigator.clipboard.writeText(createdKey); setCopied(true); } catch { // Fallback: select text } }, [createdKey]); if (!currentOrg) { return
Select an organization to manage API keys.
; } const activeKeys = keys.filter((k) => !k.revoked_at); const revokedKeys = keys.filter((k) => k.revoked_at); const atLimit = limitInfo ? limitInfo.current >= limitInfo.limit : false; return (

API Keys

{limitInfo && ( {limitInfo.current} / {limitInfo.limit} keys ({limitInfo.plan} plan) )}
{error && (
{error}
)} {/* Newly created key banner */} {createdKey && (

✅ API key created! Copy it now — you won't see it again.

{createdKey}
)} {/* Create button / form */} {!showCreateForm ? ( ) : (

Create New API Key

setNewKeyName(e.target.value)} placeholder="e.g., Production Backend" className="w-full border rounded px-3 py-2 text-sm" data-testid="key-name-input" />
)} {/* Active keys table */} {loading ? (

Loading...

) : ( <>

Active Keys ({activeKeys.length})

{activeKeys.length === 0 ? (

No active API keys. Create one to get started.

) : ( {activeKeys.map((key) => ( ))}
Prefix Name Environment Last Used Created
{key.key_prefix}… {key.name} {ENV_LABELS[key.environment]} {key.last_used_at ? new Date(key.last_used_at).toLocaleDateString() : 'Never'} {new Date(key.created_at).toLocaleDateString()}
)} {/* Revoked keys (collapsed) */} {revokedKeys.length > 0 && (
Revoked Keys ({revokedKeys.length}) {revokedKeys.map((key) => ( ))}
Prefix Name Environment Revoked
{key.key_prefix}… {key.name} {ENV_LABELS[key.environment]} {key.revoked_at ? new Date(key.revoked_at).toLocaleDateString() : '—'}
)} )} {/* Revoke confirmation dialog */} {revokeTarget && (

Revoke API Key?

This will permanently revoke {revokeTarget.name} ({revokeTarget.key_prefix}…). Any services using this key will stop working immediately.

)}
); } export default ApiKeyManagement;