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
| Prop | Type | Default | Description |
|---|---|---|---|
token | string | required | Portal authentication token (whpt_xxx) |
apiUrl | string | 'https://api.hookbase.app' | Hookbase API base URL |
theme | HookbaseTheme | {} | Theme customization |
children | ReactNode | required | Child components |
onError | (error: Error) => void | — | Global 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
| Prop | Type | Default | Description |
|---|---|---|---|
showActions | boolean | true | Show action buttons (Send Test, Verify, Retry All Failed, Edit, Delete) |
emptyState | ReactNode | Default empty state | Custom content when no endpoints exist |
onEndpointClick | (endpoint: Endpoint) => void | — | Called when an endpoint card is clicked |
onEditClick | (endpoint: Endpoint) => void | — | Called when Edit button is clicked |
onDeleteClick | (endpoint: Endpoint) => void | — | Called when Delete button is clicked |
className | string | — | Additional 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
| Prop | Type | Default | Description |
|---|---|---|---|
mode | 'create' | 'edit' | 'create' | Form mode |
endpoint | Endpoint | — | Endpoint to edit (required when mode is 'edit') |
onSuccess | (endpoint: Endpoint, secret?: string) => void | — | Called on success. secret is only provided in create mode. |
onCancel | () => void | — | Called when Cancel is clicked |
className | string | — | Additional 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
| Prop | Type | Description |
|---|---|---|
endpoint | Endpoint | required — The endpoint whose secret to rotate |
onSuccess | (newSecret: string) => void | Called after successful rotation |
onCancel | () => void | Called 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
| Prop | Type | Default | Description |
|---|---|---|---|
onEventTypeClick | (eventType: EventType) => void | — | Called when an event type is clicked |
selectedIds | Set<string> | — | Event type IDs to mark as selected/subscribed |
showSearch | boolean | true | Show search input |
className | string | — | Additional 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
| Prop | Type | Default | Description |
|---|---|---|---|
endpointId | string | required | Endpoint to manage subscriptions for |
variant | 'full' | 'compact' | 'full' | full shows categorized card layout; compact shows a flat list |
onChange | (subscribedEventTypeIds: string[]) => void | — | Called when subscriptions change |
className | string | — | Additional 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
| Prop | Type | Default | Description |
|---|---|---|---|
endpointId | string | — | Filter by endpoint |
status | 'pending' | 'success' | 'failed' | 'exhausted' | — | Filter by status |
limit | number | 25 | Messages per page |
refreshInterval | number | 0 | Auto-refresh interval in ms (0 = disabled) |
onMessageClick | (message: OutboundMessage) => void | — | Called when a message row is clicked |
className | string | — | Additional 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
refreshIntervalis 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;
}