Components

All components must be rendered inside a <HookbasePortal> provider. Import the default styles or provide your own (see Styling).

import '@hookbase/portal/styles.css';

HookbasePortal

The root provider that initializes the API client, query cache, and theme. Wrap all other portal components with this.

import { HookbasePortal } from '@hookbase/portal';
 
<HookbasePortal
  token="whpt_abc123..."
  apiUrl="https://api.hookbase.app"
  theme=
  onError={(err) => console.error(err)}
>
  {children}
</HookbasePortal>

Props — HookbasePortalProps

PropTypeDefaultDescription
tokenstringrequiredPortal authentication token (whpt_xxx)
apiUrlstring'https://api.hookbase.app'Hookbase API base URL
themeHookbaseTheme{}Theme customization
childrenReactNoderequiredChild components
onError(error: Error) => voidGlobal error callback for mutation errors

EndpointList

Displays a list of the customer's webhook endpoints with status badges, delivery stats, and action buttons for testing, verifying, and retrying failed messages.

import { EndpointList } from '@hookbase/portal';
 
<EndpointList
  showActions={true}
  onEndpointClick={(endpoint) => setSelected(endpoint)}
  onEditClick={(endpoint) => openEditModal(endpoint)}
  onDeleteClick={(endpoint) => confirmDelete(endpoint)}
  emptyState={<MyCustomEmptyState />}
/>

Props — EndpointListProps

PropTypeDefaultDescription
showActionsbooleantrueShow action buttons (Send Test, Verify, Retry All Failed, Edit, Delete)
emptyStateReactNodeDefault empty stateCustom content when no endpoints exist
onEndpointClick(endpoint: Endpoint) => voidCalled when an endpoint card is clicked
onEditClick(endpoint: Endpoint) => voidCalled when Edit button is clicked
onDeleteClick(endpoint: Endpoint) => voidCalled when Delete button is clicked
classNamestringAdditional CSS class

Built-in Actions

When showActions is true, each endpoint card includes:

  • Send Test — fires a test event to the endpoint and shows inline results (status code, response time)
  • Verify — sends a challenge request to confirm endpoint ownership (shown only for unverified endpoints)
  • Retry All Failed — bulk-replays all failed/exhausted messages for the endpoint (shown only when failures exist)
  • Edit / Delete — trigger the corresponding callback props

EndpointForm

Form for creating or editing webhook endpoints. On creation, displays a dialog with the signing secret.

import { EndpointForm } from '@hookbase/portal';
 
<EndpointForm
  mode="create"
  onSuccess={(endpoint, secret) => {
    console.log('Created:', endpoint.url);
    console.log('Secret:', secret); // Only on create
  }}
  onCancel={() => setShowForm(false)}
/>

Props — EndpointFormProps

PropTypeDefaultDescription
mode'create' | 'edit''create'Form mode
endpointEndpointEndpoint to edit (required when mode is 'edit')
onSuccess(endpoint: Endpoint, secret?: string) => voidCalled on success. secret is only provided in create mode.
onCancel() => voidCalled when Cancel is clicked
classNamestringAdditional CSS class

EndpointSecretRotation

UI for rotating an endpoint's signing secret. Shows a confirmation card, then displays the new secret in a dialog.

import { EndpointSecretRotation } from '@hookbase/portal';
 
<EndpointSecretRotation
  endpoint={selectedEndpoint}
  onSuccess={(newSecret) => console.log('New secret:', newSecret)}
  onCancel={() => setShowRotation(false)}
/>

Props — EndpointSecretRotationProps

PropTypeDescription
endpointEndpointrequired — The endpoint whose secret to rotate
onSuccess(newSecret: string) => voidCalled after successful rotation
onCancel() => voidCalled when Cancel is clicked

EventTypeList

Displays available event types grouped by category with search, expandable example payloads, and documentation links.

import { EventTypeList } from '@hookbase/portal';
 
<EventTypeList
  showSearch={true}
  selectedIds={subscribedEventTypeIds}
  onEventTypeClick={(eventType) => toggleSubscription(eventType)}
/>

Props — EventTypeListProps

PropTypeDefaultDescription
onEventTypeClick(eventType: EventType) => voidCalled when an event type is clicked
selectedIdsSet<string>Event type IDs to mark as selected/subscribed
showSearchbooleantrueShow search input
classNamestringAdditional CSS class

SubscriptionManager

Toggle event type subscriptions for an endpoint. Groups event types by category with switch controls.

import { SubscriptionManager } from '@hookbase/portal';
 
<SubscriptionManager
  endpointId={selectedEndpoint.id}
  variant="full"
  onChange={(subscribedIds) => console.log('Subscribed:', subscribedIds)}
/>

Props — SubscriptionManagerProps

PropTypeDefaultDescription
endpointIdstringrequiredEndpoint to manage subscriptions for
variant'full' | 'compact''full'full shows categorized card layout; compact shows a flat list
onChange(subscribedEventTypeIds: string[]) => voidCalled when subscriptions change
classNamestringAdditional CSS class

MessageLog

Paginated delivery history with status badges, relative timestamps, and per-message retry buttons.

import { MessageLog } from '@hookbase/portal';
 
<MessageLog
  endpointId={selectedEndpoint?.id}
  status="failed"
  limit={25}
  refreshInterval={30000}
  onMessageClick={(message) => showDetail(message)}
/>

Props — MessageLogProps

PropTypeDefaultDescription
endpointIdstringFilter by endpoint
status'pending' | 'success' | 'failed' | 'exhausted'Filter by status
limitnumber25Messages per page
refreshIntervalnumber0Auto-refresh interval in ms (0 = disabled)
onMessageClick(message: OutboundMessage) => voidCalled when a message row is clicked
classNamestringAdditional CSS class

Built-in Features

  • Retry button on failed/exhausted messages — replays the message via the portal API
  • Pagination — Previous/Next controls with "X of Y" counter
  • Auto-refresh badge — shows "Updating..." indicator when refreshInterval is set

UI Primitives

For advanced customization, the package exports low-level UI components used internally:

import {
  Button, Badge, Card, CardHeader, CardTitle, CardDescription,
  CardContent, CardFooter, Dialog, DialogTrigger, DialogContent,
  DialogHeader, DialogFooter, DialogTitle, DialogDescription,
  Input, Textarea, Label, Skeleton, SkeletonText,
  Checkbox, Switch,
} from '@hookbase/portal';

These are styled with the same CSS variables as the portal components and can be used to build custom layouts that match the portal's look and feel.


Full Example

import { useState, useEffect } from 'react';
import {
  HookbasePortal,
  EndpointList,
  EndpointForm,
  SubscriptionManager,
  MessageLog,
} from '@hookbase/portal';
import '@hookbase/portal/styles.css';
 
export function WebhookDashboard() {
  const [token, setToken] = useState<string | null>(null);
  const [selected, setSelected] = useState<Endpoint | null>(null);
  const [showCreate, setShowCreate] = useState(false);
 
  useEffect(() => {
    fetch('/api/webhook-portal-token')
      .then(res => res.json())
      .then(data => setToken(data.token));
  }, []);
 
  if (!token) return <div>Loading...</div>;
 
  return (
    <HookbasePortal token={token}>
      <div style=>
        <div>
          <h2>Endpoints</h2>
          <button onClick={() => setShowCreate(true)}>Add Endpoint</button>
 
          {showCreate && (
            <EndpointForm
              onSuccess={(ep) => { setShowCreate(false); setSelected(ep); }}
              onCancel={() => setShowCreate(false)}
            />
          )}
 
          <EndpointList
            onEndpointClick={setSelected}
          />
        </div>
 
        <div>
          {selected ? (
            <SubscriptionManager endpointId={selected.id} />
          ) : (
            <p>Select an endpoint to manage subscriptions</p>
          )}
        </div>
      </div>
 
      <MessageLog
        endpointId={selected?.id}
        limit={25}
        refreshInterval={30000}
      />
    </HookbasePortal>
  );
}

TypeScript Types

HookbaseTheme

interface HookbaseTheme {
  colors?: {
    primary?: string;
    background?: string;
    foreground?: string;
    muted?: string;
    mutedForeground?: string;
    border?: string;
    destructive?: string;
    success?: string;
    warning?: string;
  };
  borderRadius?: string;
  darkMode?: 'auto' | 'light' | 'dark';
}

Endpoint

interface Endpoint {
  id: string;
  url: string;
  description: string | null;
  isDisabled: boolean;
  circuitState: 'closed' | 'open' | 'half_open';
  totalMessages: number;
  totalSuccesses: number;
  totalFailures: number;
  isVerified: boolean;
  verifiedAt: string | null;
  createdAt: string;
  updatedAt: string;
}

OutboundMessage

interface OutboundMessage {
  id: string;
  eventType: string;
  endpointId: string;
  endpointUrl: string;
  status: 'pending' | 'success' | 'failed' | 'exhausted';
  attempts: number;
  lastAttemptAt: string | null;
  nextAttemptAt: string | null;
  createdAt: string;
}

EventType

interface EventType {
  id: string;
  name: string;
  displayName: string | null;
  description: string | null;
  category: string | null;
  schema: string | null;
  examplePayload: string | null;
  documentationUrl: string | null;
}