API Keys
API key management view component for DeveloperApp
API Keys
The APIKeys view provides a comprehensive interface for managing API authentication keys. Users can create, view, rotate, revoke, and configure API keys with scoped permissions for secure API access.
Type Signature
interface APIKeysViewProps {
/** Page title */
title?: string
/** Page description */
description?: string
/** View layout mode */
layout?: 'single' | 'column' | 'canvas' | 'vibe'
/** API keys list */
keys?: Array<{
id: string
name: string
key: string // Masked or full key
created: string
lastUsed?: string
scopes?: string[] // API scopes/permissions
expiresAt?: string
}>
/** Allow creating new keys */
allowCreate?: boolean
/** Show usage stats */
showUsage?: boolean
/** Show scopes/permissions */
showScopes?: boolean
/** Page-level action buttons */
actions?: Array<{
id: string
label: string
icon?: string
onClick?: () => void
variant?: 'default' | 'primary' | 'destructive'
}>
/** Child content */
children?: React.ReactNode
}Usage
<App name="Developer Portal">
<Shell>
<APIKeysView
title="API Keys"
description="Manage API keys for authentication"
allowCreate={true}
showUsage={true}
showScopes={true}
/>
</Shell>
</App>Props
title
The page title displayed in the header. Defaults to "API Keys".
<APIKeysView title="API Keys">description
Optional page description or subtitle.
<APIKeysView
title="API Keys"
description="Create and manage API keys for secure authentication"
>layout
Layout mode for the view content. Defaults to 'single'.
// Single column layout (default)
<APIKeysView layout="single">
// Multi-column layout
<APIKeysView layout="column">keys
Array of API key records to display.
<APIKeysView
keys={[
{
id: 'key_1',
name: 'Production API Key',
key: 'sk_live_••••••••••••4a3f',
created: '2024-01-15T10:30:00Z',
lastUsed: '2024-12-19T08:15:00Z',
scopes: ['read', 'write'],
expiresAt: '2025-01-15T10:30:00Z'
},
{
id: 'key_2',
name: 'Development API Key',
key: 'sk_test_••••••••••••8b2e',
created: '2024-06-20T14:00:00Z',
lastUsed: '2024-12-18T16:45:00Z',
scopes: ['read']
}
]}
>allowCreate
Controls whether users can create new API keys. Defaults to true.
// Allow creating new keys (default)
<APIKeysView allowCreate={true}>
// Read-only mode
<APIKeysView allowCreate={false}>showUsage
Display usage statistics for each API key. Defaults to true.
<APIKeysView showUsage={true}>
{/* Shows last used timestamp and request count */}
</APIKeysView>showScopes
Display scopes/permissions for each API key. Defaults to true.
<APIKeysView showScopes={true}>
{/* Shows permission badges for each key */}
</APIKeysView>actions
Page-level action buttons displayed in the header.
<APIKeysView
actions={[
{
id: 'create',
label: 'Create Key',
icon: 'Plus',
variant: 'primary',
onClick: () => openCreateDialog()
},
{
id: 'docs',
label: 'API Docs',
icon: 'Book',
onClick: () => navigateToDocs()
}
]}
>Key Management Operations
Creating New Keys
Users can create API keys with custom names and permissions:
const handleCreateKey = async (data: CreateKeyData) => {
const key = await createAPIKey({
name: data.name,
scopes: data.scopes,
expiresAt: data.expiresAt
})
// Show full key only once
showKeyDialog(key.key)
}
<APIKeysView
allowCreate={true}
actions={[
{
id: 'create',
label: 'Create Key',
variant: 'primary',
onClick: () => openCreateDialog()
}
]}
>
<CreateKeyDialog
onSubmit={handleCreateKey}
scopes={['read', 'write', 'admin']}
/>
</APIKeysView>Viewing Key Details
Display masked keys with option to reveal (copy) full key:
<APIKeysView>
{keys.map(key => (
<KeyCard
key={key.id}
name={key.name}
maskedKey={key.key}
onCopy={() => copyFullKey(key.id)}
onRevoke={() => revokeKey(key.id)}
/>
))}
</APIKeysView>Rotating Keys
Implement key rotation for security:
const handleRotateKey = async (keyId: string) => {
const newKey = await rotateAPIKey(keyId)
toast.success('Key rotated successfully')
showKeyDialog(newKey.key)
}
<APIKeysView>
<KeyCard
actions={[
{ label: 'Rotate', onClick: () => handleRotateKey(key.id) },
{ label: 'Revoke', onClick: () => revokeKey(key.id), variant: 'destructive' }
]}
/>
</APIKeysView>Revoking Keys
Permanently revoke API keys:
const handleRevokeKey = async (keyId: string) => {
if (confirm('Are you sure? This action cannot be undone.')) {
await revokeAPIKey(keyId)
toast.success('Key revoked')
refetchKeys()
}
}
<APIKeysView
keys={keys}
onRevoke={handleRevokeKey}
>Scopes and Permissions
Configure fine-grained permissions for each key:
<APIKeysView showScopes={true}>
<CreateKeyDialog
scopes={[
{
id: 'read',
label: 'Read',
description: 'Read access to all resources'
},
{
id: 'write',
label: 'Write',
description: 'Create and update resources'
},
{
id: 'delete',
label: 'Delete',
description: 'Delete resources'
},
{
id: 'admin',
label: 'Admin',
description: 'Full administrative access'
}
]}
/>
</APIKeysView>Usage Statistics
Track API key usage for monitoring:
<APIKeysView showUsage={true}>
<KeyCard
key={key.id}
usage={{
lastUsed: '2024-12-19T08:15:00Z',
requests: 1245,
errors: 3,
rateLimit: {
limit: 1000,
remaining: 987,
reset: '2024-12-19T09:00:00Z'
}
}}
/>
</APIKeysView>Expiration Management
Set expiration dates for temporary keys:
<CreateKeyDialog
onSubmit={async (data) => {
await createAPIKey({
name: data.name,
scopes: data.scopes,
expiresAt: addDays(new Date(), 30) // Expire in 30 days
})
}}
>
<FormField
label="Expiration"
name="expiresAt"
type="date"
description="Leave empty for non-expiring keys"
/>
</CreateKeyDialog>Security Best Practices
Key Display
Show full key only once during creation:
const [newKey, setNewKey] = useState<string | null>(null)
const handleCreateKey = async (data: CreateKeyData) => {
const key = await createAPIKey(data)
setNewKey(key.key) // Show full key
}
<APIKeysView>
{newKey && (
<Dialog open onClose={() => setNewKey(null)}>
<DialogTitle>API Key Created</DialogTitle>
<DialogContent>
<Alert variant="warning">
Save this key now. You won't be able to see it again.
</Alert>
<CodeBlock code={newKey} copyable />
</DialogContent>
</Dialog>
)}
</APIKeysView>Key Masking
Always mask keys in the UI:
const maskKey = (key: string) => {
const prefix = key.slice(0, 7) // e.g., 'sk_live_'
const suffix = key.slice(-4)
return `${prefix}${'•'.repeat(20)}${suffix}`
}
<APIKeysView
keys={keys.map(key => ({
...key,
key: maskKey(key.key)
}))}
>Confirmation Dialogs
Require confirmation for destructive actions:
<APIKeysView>
<ConfirmDialog
title="Revoke API Key?"
description="This will immediately stop all requests using this key. This action cannot be undone."
confirmText="Revoke Key"
variant="destructive"
onConfirm={() => revokeKey(keyId)}
/>
</APIKeysView>Layout Examples
Table Layout
Display keys in a table for many keys:
<APIKeysView layout="single">
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Key</TableHead>
<TableHead>Created</TableHead>
<TableHead>Last Used</TableHead>
<TableHead>Scopes</TableHead>
<TableHead>Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{keys.map(key => (
<TableRow key={key.id}>
<TableCell>{key.name}</TableCell>
<TableCell><Code>{key.key}</Code></TableCell>
<TableCell>{formatDate(key.created)}</TableCell>
<TableCell>{formatDate(key.lastUsed)}</TableCell>
<TableCell><ScopeBadges scopes={key.scopes} /></TableCell>
<TableCell><KeyActions keyId={key.id} /></TableCell>
</TableRow>
))}
</TableBody>
</Table>
</APIKeysView>Card Layout
Use cards for better visual hierarchy:
<APIKeysView layout="single">
<div className="grid gap-4">
{keys.map(key => (
<Card key={key.id}>
<CardHeader>
<CardTitle>{key.name}</CardTitle>
<CardDescription>
Created {formatDate(key.created)}
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex items-center gap-2">
<Code>{key.key}</Code>
<Button size="sm" onClick={() => copyKey(key.id)}>
Copy
</Button>
</div>
{key.scopes && (
<div className="mt-2">
<ScopeBadges scopes={key.scopes} />
</div>
)}
</CardContent>
<CardFooter>
<Button variant="ghost" onClick={() => rotateKey(key.id)}>
Rotate
</Button>
<Button variant="destructive" onClick={() => revokeKey(key.id)}>
Revoke
</Button>
</CardFooter>
</Card>
))}
</div>
</APIKeysView>Empty State
Handle cases with no API keys:
<APIKeysView>
{keys.length === 0 ? (
<EmptyState
icon="Key"
title="No API keys yet"
description="Create your first API key to start making authenticated requests to the API"
action={{
label: 'Create API Key',
onClick: () => openCreateDialog()
}}
/>
) : (
<KeysList keys={keys} />
)}
</APIKeysView>Integration with Payload CMS
Auto-wire to Payload apiKeys collection:
// Auto-fetches from Payload 'apiKeys' collection
<APIKeysView collection="apiKeys">
{/* Keys loaded automatically */}
</APIKeysView>DeveloperApp Integration
Part of the DeveloperApp component suite:
<DeveloperDashboard
config={{
showAPIKeys: true,
showWebhooks: true,
showUsage: true,
showLogs: true
}}
>
<APIKeysView />
<WebhooksView />
<UsageView />
<LogsView />
</DeveloperDashboard>Responsive Behavior
The view adapts to screen size:
Desktop (≥1024px)
┌────────────────────────────────────────┐
│ API Keys [Create] │
├────────────────────────────────────────┤
│ ┌──────────────────────────────────┐ │
│ │ Production API Key │ │
│ │ sk_live_••••••••••••4a3f [Copy] │ │
│ │ Created: Jan 15, 2024 │ │
│ │ Last used: 2 hours ago │ │
│ │ [read] [write] │ │
│ │ [Rotate] [Revoke] │ │
│ └──────────────────────────────────┘ │
└────────────────────────────────────────┘
Full card layoutMobile (under 768px)
┌────────────────────────────┐
│ API Keys [+] │
├────────────────────────────┤
│ ┌────────────────────┐ │
│ │ Production API Key │ │
│ │ sk_live_••••4a3f │ │
│ │ Jan 15, 2024 │ │
│ │ [Rotate] [Revoke] │ │
│ └────────────────────┘ │
└────────────────────────────┘
Compact card layoutAccessibility
The APIKeys view implements accessibility features:
- Keyboard navigation - Tab through keys and actions
- Screen reader labels - Descriptive labels for keys and actions
- Focus management - Focus moves to newly created key
- Copy feedback - Announce when key is copied
<APIKeysView aria-label="API key management">
<Button
aria-label="Create new API key"
onClick={openCreateDialog}
>
Create Key
</Button>
<KeyCard
aria-label={`API key ${key.name}, created ${formatDate(key.created)}`}
>
<Button
aria-label={`Copy ${key.name} to clipboard`}
onClick={() => copyKey(key.id)}
>
Copy
</Button>
</KeyCard>
</APIKeysView>