Hooks
All hooks must be used inside a <HookbasePortal> provider. They provide data fetching, caching (via React Query), and mutation methods for building custom portal UIs.
useEndpoints
Fetches all endpoints and provides CRUD + secret rotation methods.
const {
// Query
endpoints, // Endpoint[]
isLoading, // boolean
isError, // boolean
error, // Error | null
refetch, // () => Promise<...>
// Mutations
createEndpoint, // (data: CreateEndpointInput) => Promise<ApiResponse<EndpointWithSecret>>
isCreating, // boolean
createError, // Error | null
updateEndpoint, // (id: string, data: UpdateEndpointInput) => Promise<ApiResponse<Endpoint>>
isUpdating, // boolean
updateError, // Error | null
deleteEndpoint, // (id: string) => Promise<void>
isDeleting, // boolean
deleteError, // Error | null
rotateSecret, // (id: string) => Promise<ApiResponse<{ secret: string }>>
isRotating, // boolean
} = useEndpoints();Example — Custom Create Form
function CreateEndpointButton() {
const { createEndpoint, isCreating } = useEndpoints();
const handleCreate = async () => {
const result = await createEndpoint({
url: 'https://example.com/webhooks',
description: 'Production endpoint',
});
// result.data.secret — save this! Only shown once.
alert(`Secret: ${result.data.secret}`);
};
return (
<button onClick={handleCreate} disabled={isCreating}>
{isCreating ? 'Creating...' : 'Create Endpoint'}
</button>
);
}Input Types
interface CreateEndpointInput {
url: string;
description?: string;
}
interface UpdateEndpointInput {
url?: string;
description?: string;
isDisabled?: boolean;
}useEndpoint
Fetches a single endpoint by ID.
const {
data, // Endpoint | undefined
isLoading, // boolean
isError, // boolean
error, // Error | null
} = useEndpoint(endpointId);The query is disabled when endpointId is falsy.
useEventTypes
Fetches available event types with automatic category grouping.
const {
eventTypes, // EventType[]
groupedByCategory, // Record<string, EventType[]>
categories, // string[] (sorted)
isLoading, // boolean
isError, // boolean
error, // Error | null
refetch, // () => Promise<...>
} = useEventTypes();Results are cached for 5 minutes (staleTime: 300000).
Example — Category List
function EventCatalog() {
const { groupedByCategory, categories, isLoading } = useEventTypes();
if (isLoading) return <p>Loading...</p>;
return (
<div>
{categories.map(category => (
<div key={category}>
<h3>{category}</h3>
<ul>
{groupedByCategory[category].map(et => (
<li key={et.id}>
<code>{et.name}</code> — {et.description}
</li>
))}
</ul>
</div>
))}
</div>
);
}useSubscriptions
Manages event type subscriptions for an endpoint.
const {
// Query
subscriptions, // Subscription[]
subscribedEventTypeIds, // Set<string>
isLoading, // boolean
isError, // boolean
error, // Error | null
refetch, // () => Promise<...>
// Mutations
createSubscription, // (data: CreateSubscriptionInput) => Promise<ApiResponse<Subscription>>
isCreating, // boolean
createError, // Error | null
deleteSubscription, // (id: string) => Promise<void>
isDeleting, // boolean
deleteError, // Error | null
// Convenience
toggleSubscription, // (eventTypeId: string, endpointId: string) => Promise<void>
} = useSubscriptions(endpointId?);Example — Toggle Subscriptions
function SubscriptionToggle({ endpointId, eventType }) {
const { subscribedEventTypeIds, toggleSubscription } = useSubscriptions(endpointId);
const isSubscribed = subscribedEventTypeIds.has(eventType.id);
return (
<label>
<input
type="checkbox"
checked={isSubscribed}
onChange={() => toggleSubscription(eventType.id, endpointId)}
/>
{eventType.name}
</label>
);
}useMessages
Fetches paginated delivery messages with optional filtering and auto-refresh.
const {
messages, // OutboundMessage[]
total, // number
isLoading, // boolean
isError, // boolean
error, // Error | null
refetch, // () => Promise<...>
isFetching, // boolean (true during background refetch)
} = useMessages(options?);Options — UseMessagesOptions
| Option | Type | Default | Description |
|---|---|---|---|
endpointId | string | — | Filter by endpoint |
status | 'pending' | 'success' | 'failed' | 'exhausted' | — | Filter by status |
limit | number | — | Page size |
offset | number | — | Pagination offset |
refreshInterval | number | 0 | Auto-refresh interval in ms |
Example
function FailedMessages({ endpointId }) {
const { messages, total, isLoading } = useMessages({
endpointId,
status: 'failed',
limit: 10,
refreshInterval: 15000,
});
if (isLoading) return <p>Loading...</p>;
return (
<div>
<p>{total} failed messages</p>
{messages.map(msg => (
<div key={msg.id}>
<code>{msg.eventType}</code> — {msg.attempts} attempts
</div>
))}
</div>
);
}useMessage
Fetches a single message by ID.
const {
data, // OutboundMessage | undefined
isLoading, // boolean
isError, // boolean
error, // Error | null
} = useMessage(messageId);useApplication
Fetches the current application info (name, ID, organization).
const {
application, // Application | undefined
isLoading, // boolean
isError, // boolean
error, // Error | null
refetch, // () => Promise<...>
} = useApplication();Application Type
interface Application {
id: string;
name: string;
organizationId: string;
}useTestEndpoint
Sends a test event to an endpoint and tracks the result.
const {
testEndpoint, // (endpointId: string, eventType?: string) => Promise<TestDeliveryResult>
isTesting, // boolean
testResult, // TestDeliveryResult | null
testError, // Error | null
clearResult, // () => void
} = useTestEndpoint();TestDeliveryResult
interface TestDeliveryResult {
success: boolean;
testEvent: {
id: string;
type: string;
timestamp: string;
};
delivery: {
status: 'success' | 'failed';
responseStatus: number;
responseTimeMs: number;
responseBody: string;
errorMessage: string | null;
};
signature: {
header: string;
value: string;
timestamp: number;
};
}Example
function TestButton({ endpointId }) {
const { testEndpoint, isTesting, testResult } = useTestEndpoint();
return (
<div>
<button
onClick={() => testEndpoint(endpointId)}
disabled={isTesting}
>
{isTesting ? 'Sending...' : 'Send Test Event'}
</button>
{testResult && (
<p>
{testResult.success ? 'Success' : 'Failed'} —
{testResult.delivery.responseStatus} in {testResult.delivery.responseTimeMs}ms
</p>
)}
</div>
);
}useReplay
Replays failed messages — single message or bulk for an endpoint.
const {
replayMessage, // (messageId: string) => Promise<ReplayResult>
replayFailedForEndpoint, // (endpointId: string) => Promise<BulkReplayResult>
isReplaying, // boolean
replayError, // Error | null
} = useReplay();Return Types
interface ReplayResult {
originalMessageId: string;
newMessageId: string;
status: string;
}
interface BulkReplayResult {
replayed: number;
newMessageIds: string[];
}Example
function RetryButton({ message }) {
const { replayMessage, isReplaying } = useReplay();
return (
<button
onClick={() => replayMessage(message.id)}
disabled={isReplaying || message.status === 'success'}
>
{isReplaying ? 'Retrying...' : 'Retry'}
</button>
);
}useHookbase
Access the low-level portal context (API client, token, base URL).
const {
api, // PortalApiClient
token, // string
apiUrl, // string
} = useHookbase();Use this when you need to call the API client directly, for example to verify an endpoint:
const { api } = useHookbase();
const result = await api.verifyEndpoint(endpointId);See the Portal API Reference for all available client methods.
useTheme
Access the current theme and dark mode state.
const {
theme, // HookbaseTheme
isDark, // boolean
} = useTheme();