mdxui

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 layout

Mobile (under 768px)

┌────────────────────────────┐
│  API Keys          [+]     │
├────────────────────────────┤
│  ┌────────────────────┐    │
│  │ Production API Key │    │
│  │ sk_live_••••4a3f   │    │
│  │ Jan 15, 2024       │    │
│  │ [Rotate] [Revoke]  │    │
│  └────────────────────┘    │
└────────────────────────────┘
Compact card layout

Accessibility

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>
  • Webhooks - Webhook configuration view
  • Usage - API usage metrics view
  • Logs - API request logs view

On this page