mdxui

Webhooks

Webhook endpoint configuration view component for DeveloperApp

Webhooks

The Webhooks view provides an interface for configuring webhook endpoints, managing event subscriptions, viewing delivery logs, and testing webhook integrations.

Type Signature

interface WebhooksViewProps {
  /** Page title */
  title?: string
  /** Page description */
  description?: string
  /** View layout mode */
  layout?: 'single' | 'column' | 'canvas' | 'vibe'
  /** Webhook endpoints */
  webhooks?: Array<{
    id: string
    url: string // Webhook URL
    events: string[] // Subscribed event types
    active: boolean
    secret?: string // Webhook signing secret
    created: string
  }>
  /** Recent deliveries */
  deliveries?: Array<{
    id: string
    event: string
    status: number // HTTP status code
    timestamp: string
    duration?: number // Response time in ms
  }>
  /** Allow testing webhooks */
  allowTest?: boolean
  /** Show delivery logs */
  showLogs?: 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>
    <WebhooksView
      title="Webhooks"
      description="Configure webhook endpoints for event notifications"
      allowTest={true}
      showLogs={true}
    />
  </Shell>
</App>

Props

title

The page title displayed in the header. Defaults to "Webhooks".

<WebhooksView title="Webhooks">

description

Optional page description or subtitle.

<WebhooksView
  title="Webhooks"
  description="Receive real-time notifications when events occur in your account"
>

layout

Layout mode for the view content. Defaults to 'single'.

// Single column layout (default)
<WebhooksView layout="single">

// Multi-column layout
<WebhooksView layout="column">

webhooks

Array of configured webhook endpoints.

<WebhooksView
  webhooks={[
    {
      id: 'wh_1',
      url: 'https://api.example.com/webhooks',
      events: ['user.created', 'user.updated', 'payment.success'],
      active: true,
      secret: 'whsec_••••••••••••8a3f',
      created: '2024-01-15T10:30:00Z'
    },
    {
      id: 'wh_2',
      url: 'https://staging.example.com/webhooks',
      events: ['user.created'],
      active: false,
      created: '2024-06-20T14:00:00Z'
    }
  ]}
>

deliveries

Recent webhook delivery attempts with status codes.

<WebhooksView
  deliveries={[
    {
      id: 'del_1',
      event: 'user.created',
      status: 200,
      timestamp: '2024-12-19T08:15:00Z',
      duration: 145
    },
    {
      id: 'del_2',
      event: 'payment.success',
      status: 500,
      timestamp: '2024-12-19T08:10:00Z',
      duration: 2340
    }
  ]}
>

allowTest

Enable webhook testing functionality. Defaults to true.

<WebhooksView allowTest={true}>
  {/* Shows "Test" button for each webhook */}
</WebhooksView>

showLogs

Display delivery logs for debugging. Defaults to true.

<WebhooksView showLogs={true}>
  {/* Shows recent delivery attempts */}
</WebhooksView>

actions

Page-level action buttons displayed in the header.

<WebhooksView
  actions={[
    {
      id: 'create',
      label: 'Add Endpoint',
      icon: 'Plus',
      variant: 'primary',
      onClick: () => openCreateDialog()
    },
    {
      id: 'docs',
      label: 'Webhook Docs',
      icon: 'Book',
      onClick: () => navigateToDocs()
    }
  ]}
>

Webhook Management

Creating Endpoints

Configure new webhook endpoints with event subscriptions:

const handleCreateWebhook = async (data: CreateWebhookData) => {
  const webhook = await createWebhook({
    url: data.url,
    events: data.events,
    active: true
  })

  toast.success('Webhook created')
  refetchWebhooks()
}

<WebhooksView>
  <CreateWebhookDialog
    onSubmit={handleCreateWebhook}
    events={[
      'user.created',
      'user.updated',
      'user.deleted',
      'payment.success',
      'payment.failed'
    ]}
  />
</WebhooksView>

Updating Endpoints

Edit existing webhook configurations:

const handleUpdateWebhook = async (id: string, data: UpdateWebhookData) => {
  await updateWebhook(id, {
    url: data.url,
    events: data.events,
    active: data.active
  })

  toast.success('Webhook updated')
}

<WebhooksView>
  <EditWebhookDialog
    webhook={selectedWebhook}
    onSubmit={(data) => handleUpdateWebhook(selectedWebhook.id, data)}
  />
</WebhooksView>

Deleting Endpoints

Remove webhook endpoints:

const handleDeleteWebhook = async (id: string) => {
  if (confirm('Delete this webhook endpoint?')) {
    await deleteWebhook(id)
    toast.success('Webhook deleted')
    refetchWebhooks()
  }
}

<WebhooksView
  webhooks={webhooks}
  onDelete={handleDeleteWebhook}
>

Event Subscriptions

Subscribe to specific events:

<CreateWebhookDialog
  events={[
    {
      id: 'user.created',
      label: 'User Created',
      description: 'Triggered when a new user is created'
    },
    {
      id: 'user.updated',
      label: 'User Updated',
      description: 'Triggered when a user profile is updated'
    },
    {
      id: 'payment.success',
      label: 'Payment Success',
      description: 'Triggered when a payment is successfully processed'
    },
    {
      id: 'payment.failed',
      label: 'Payment Failed',
      description: 'Triggered when a payment fails'
    }
  ]}
>
  <FormField
    label="Events"
    name="events"
    type="checkbox-group"
    description="Select which events should trigger this webhook"
  />
</CreateWebhookDialog>

Testing Webhooks

Send test events to verify webhook configuration:

const handleTestWebhook = async (webhookId: string, event: string) => {
  const result = await testWebhook(webhookId, {
    event,
    data: generateSamplePayload(event)
  })

  if (result.status === 200) {
    toast.success('Test webhook delivered successfully')
  } else {
    toast.error(`Test failed with status ${result.status}`)
  }
}

<WebhooksView allowTest={true}>
  <WebhookCard
    webhook={webhook}
    actions={[
      {
        label: 'Test',
        onClick: () => openTestDialog(webhook.id)
      }
    ]}
  />

  <TestWebhookDialog
    webhook={webhook}
    events={webhook.events}
    onTest={handleTestWebhook}
  />
</WebhooksView>

Delivery Logs

View webhook delivery history and debug failures:

<WebhooksView showLogs={true}>
  <DeliveryLogsTable
    deliveries={deliveries}
    columns={[
      'event',
      'status',
      'timestamp',
      'duration',
      'actions'
    ]}
    onRetry={(deliveryId) => retryDelivery(deliveryId)}
    onViewDetails={(deliveryId) => showDeliveryDetails(deliveryId)}
  />
</WebhooksView>

Delivery Details

Show detailed delivery information:

<DeliveryDetailsDialog
  delivery={{
    id: 'del_1',
    event: 'user.created',
    url: 'https://api.example.com/webhooks',
    status: 200,
    timestamp: '2024-12-19T08:15:00Z',
    duration: 145,
    request: {
      headers: {
        'Content-Type': 'application/json',
        'X-Webhook-Signature': 'sha256=...'
      },
      body: {
        event: 'user.created',
        data: { id: 'usr_123', email: 'user@example.com' }
      }
    },
    response: {
      status: 200,
      headers: { 'Content-Type': 'application/json' },
      body: { received: true }
    }
  }}
>
  <Tabs>
    <TabsList>
      <TabsTrigger value="request">Request</TabsTrigger>
      <TabsTrigger value="response">Response</TabsTrigger>
    </TabsList>
    <TabsContent value="request">
      <CodeBlock code={JSON.stringify(delivery.request, null, 2)} language="json" />
    </TabsContent>
    <TabsContent value="response">
      <CodeBlock code={JSON.stringify(delivery.response, null, 2)} language="json" />
    </TabsContent>
  </Tabs>
</DeliveryDetailsDialog>

Webhook Signatures

Secure webhooks with signing secrets:

<WebhooksView>
  <WebhookCard webhook={webhook}>
    <div className="mt-4">
      <Label>Signing Secret</Label>
      <div className="flex items-center gap-2">
        <Code>{webhook.secret}</Code>
        <Button size="sm" onClick={() => copySecret(webhook.id)}>
          Copy
        </Button>
        <Button size="sm" onClick={() => rotateSecret(webhook.id)}>
          Rotate
        </Button>
      </div>
      <p className="text-sm text-muted-foreground mt-1">
        Use this secret to verify webhook signatures
      </p>
    </div>
  </WebhookCard>
</WebhooksView>

Signature Verification Example

Show code examples for verifying signatures:

<WebhooksView>
  <DocsSection title="Verifying Signatures">
    <Tabs>
      <TabsList>
        <TabsTrigger value="node">Node.js</TabsTrigger>
        <TabsTrigger value="python">Python</TabsTrigger>
        <TabsTrigger value="php">PHP</TabsTrigger>
      </TabsList>
      <TabsContent value="node">
        <CodeBlock language="javascript" code={`
const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const hash = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return signature === \`sha256=\${hash}\`;
}
        `} />
      </TabsContent>
    </Tabs>
  </DocsSection>
</WebhooksView>

Retry Logic

Handle failed deliveries with automatic retries:

<WebhooksView>
  <WebhookCard webhook={webhook}>
    <div className="mt-4">
      <Label>Retry Settings</Label>
      <FormField
        label="Max Retries"
        name="maxRetries"
        type="number"
        defaultValue={3}
        description="Number of retry attempts for failed deliveries"
      />
      <FormField
        label="Retry Delay"
        name="retryDelay"
        type="select"
        options={[
          { value: '60', label: '1 minute' },
          { value: '300', label: '5 minutes' },
          { value: '900', label: '15 minutes' }
        ]}
      />
    </div>
  </WebhookCard>
</WebhooksView>

Active/Inactive Webhooks

Toggle webhook status:

const handleToggleWebhook = async (id: string, active: boolean) => {
  await updateWebhook(id, { active })
  toast.success(active ? 'Webhook activated' : 'Webhook paused')
}

<WebhooksView>
  <WebhookCard webhook={webhook}>
    <Switch
      checked={webhook.active}
      onCheckedChange={(active) => handleToggleWebhook(webhook.id, active)}
      label="Active"
    />
  </WebhookCard>
</WebhooksView>

Layout Examples

List Layout

Display webhooks in a list with delivery stats:

<WebhooksView layout="single">
  <div className="space-y-4">
    {webhooks.map(webhook => (
      <Card key={webhook.id}>
        <CardHeader>
          <div className="flex items-center justify-between">
            <CardTitle>{webhook.url}</CardTitle>
            <Switch
              checked={webhook.active}
              onCheckedChange={(active) => toggleWebhook(webhook.id, active)}
            />
          </div>
        </CardHeader>
        <CardContent>
          <div className="space-y-2">
            <div>
              <Label>Events</Label>
              <div className="flex flex-wrap gap-1 mt-1">
                {webhook.events.map(event => (
                  <Badge key={event} variant="secondary">{event}</Badge>
                ))}
              </div>
            </div>
            <div className="flex items-center gap-4 text-sm text-muted-foreground">
              <span>Created {formatDate(webhook.created)}</span>
              <span>{deliveryCount} deliveries</span>
              <span>{successRate}% success</span>
            </div>
          </div>
        </CardContent>
        <CardFooter>
          <Button variant="ghost" onClick={() => testWebhook(webhook.id)}>
            Test
          </Button>
          <Button variant="ghost" onClick={() => editWebhook(webhook.id)}>
            Edit
          </Button>
          <Button variant="destructive" onClick={() => deleteWebhook(webhook.id)}>
            Delete
          </Button>
        </CardFooter>
      </Card>
    ))}
  </div>
</WebhooksView>

Split View

Show webhooks and recent deliveries side-by-side:

<WebhooksView layout="column">
  <div className="grid grid-cols-2 gap-6">
    <div>
      <h3 className="text-lg font-semibold mb-4">Endpoints</h3>
      <WebhooksList webhooks={webhooks} />
    </div>
    <div>
      <h3 className="text-lg font-semibold mb-4">Recent Deliveries</h3>
      <DeliveryLogsTable deliveries={recentDeliveries} />
    </div>
  </div>
</WebhooksView>

Empty State

Handle cases with no webhooks configured:

<WebhooksView>
  {webhooks.length === 0 ? (
    <EmptyState
      icon="Webhook"
      title="No webhooks configured"
      description="Add a webhook endpoint to receive real-time notifications when events occur"
      action={{
        label: 'Add Endpoint',
        onClick: () => openCreateDialog()
      }}
    />
  ) : (
    <WebhooksList webhooks={webhooks} />
  )}
</WebhooksView>

Integration with Payload CMS

Auto-wire to Payload webhooks collection:

// Auto-fetches from Payload 'webhooks' collection
<WebhooksView collection="webhooks">
  {/* Webhooks loaded automatically */}
</WebhooksView>

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)

┌────────────────────────────────────────┐
│  Webhooks                 [Add]        │
├────────────────────────────────────────┤
│  ┌──────────────────────────────────┐  │
│  │ https://api.example.com/webhooks │  │
│  │ [user.created] [payment.success] │  │
│  │ Active • Created Jan 15, 2024    │  │
│  │ 1,245 deliveries • 99.8% success │  │
│  │      [Test]  [Edit]  [Delete]    │  │
│  └──────────────────────────────────┘  │
└────────────────────────────────────────┘
Full card layout

Mobile (under 768px)

┌────────────────────────────┐
│  Webhooks          [+]     │
├────────────────────────────┤
│  ┌────────────────────┐    │
│  │ api.example.com    │    │
│  │ [user.created]     │    │
│  │ Active • Jan 15    │    │
│  │ [Test] [Edit]      │    │
│  └────────────────────┘    │
└────────────────────────────┘
Compact card layout

Accessibility

The Webhooks view implements accessibility features:

  • Keyboard navigation - Tab through webhooks and actions
  • Screen reader labels - Descriptive labels for webhooks and status
  • Focus management - Focus moves to newly created webhook
  • Status announcements - Announce delivery success/failure
<WebhooksView aria-label="Webhook endpoint management">
  <WebhookCard
    aria-label={`Webhook endpoint ${webhook.url}, ${webhook.active ? 'active' : 'inactive'}`}
  >
    <Switch
      aria-label={`Toggle webhook ${webhook.url}`}
      checked={webhook.active}
      onCheckedChange={toggleWebhook}
    />
  </WebhookCard>
</WebhooksView>
  • APIKeys - API key management view
  • Usage - API usage metrics view
  • Logs - API request logs view

On this page