Integrations
Use the agent, webhook, and API auth hooks that sit on top of the shared client layer.
Agents
useAgentSession()
useAgentSession() is the entry hook for the agents surface. It resolves the current agent session, returns the actor and the available agents for that session, and optionally exchanges a ticket before it loads the session.
import { useAgentSession } from '@wacht/nextjs';export default function AgentShell({ ticket,}: { ticket?: string | null;}) { const { hasSession, sessionLoading, sessionError, actor, agents, ticketExchanged, } = useAgentSession(ticket); if (sessionLoading) { return <div>Loading agent session...</div>; } if (!hasSession || sessionError) { return <div>Access required</div>; } return ( <div> <h1>{actor?.display_name ?? 'Actor'}</h1> <p>{agents.length} agents available</p> {ticket ? <p>Ticket exchanged: {String(ticketExchanged)}</p> : null} </div> );}›ticket?: string | null | undefined | undefined;
ticket?: string | null | undefined | undefined;›hasSession?: boolean | undefined;
hasSession?: boolean | undefined;›sessionLoading?: boolean | undefined;
sessionLoading?: boolean | undefined;›sessionError?: Error | null | undefined;
sessionError?: Error | null | undefined;›sessionId?: string | null | undefined;
sessionId?: string | null | undefined;›actor?: Actor | null | undefined;
actor?: Actor | null | undefined;›id?: string | undefined;
id?: string | undefined;›subject_type?: string | undefined;
subject_type?: string | undefined;›external_key?: string | undefined;
external_key?: string | undefined;›display_name?: string | undefined | undefined;
display_name?: string | undefined | undefined;›deployment_id?: string | undefined | undefined;
deployment_id?: string | undefined | undefined;›agents?: Agent[] | undefined;
agents?: Agent[] | undefined;›id?: string | undefined;
id?: string | undefined;›name?: string | undefined;
name?: string | undefined;›description?: string | undefined;
description?: string | undefined;›child_agents?: Agent[] | undefined | undefined;
child_agents?: Agent[] | undefined | undefined;›ticketExchanged?: boolean | undefined;
ticketExchanged?: boolean | undefined;›ticketLoading?: boolean | undefined;
ticketLoading?: boolean | undefined;›refetch?: () => Promise<void> | undefined;
refetch?: () => Promise<void> | undefined;useActorProjects()
useActorProjects() is the main project hook for the agents surface. It returns the current actor project list and the project-level mutations that create, update, archive, and unarchive those projects.
import { useActorProjects } from '@wacht/nextjs';export default function ProjectsPage() { const { projects, loading, createProject } = useActorProjects(); if (loading) { return <div>Loading projects...</div>; } return ( <div> <button onClick={async () => { await createProject({ name: 'Planning', agent_id: 'agent_123', }); }} > Create project </button> <ul> {projects.map((project) => ( <li key={project.id}>{project.name}</li> ))} </ul> </div> );}›options?: { includeArchived?: boolean; enabled?: boolean } | undefined | undefined;
options?: { includeArchived?: boolean; enabled?: boolean } | undefined | undefined;›includeArchived?: boolean | undefined | undefined;
includeArchived?: boolean | undefined | undefined;›enabled?: boolean | undefined | undefined;
enabled?: boolean | undefined | undefined;›projects?: ActorProject[] | undefined;
projects?: ActorProject[] | undefined;›id?: string | undefined;
id?: string | undefined;›name?: string | undefined;
name?: string | undefined;›description?: string | undefined | undefined;
description?: string | undefined | undefined;›status?: string | undefined;
status?: string | undefined;›coordinator_thread_id?: string | undefined | undefined;
coordinator_thread_id?: string | undefined | undefined;›review_thread_id?: string | undefined | undefined;
review_thread_id?: string | undefined | undefined;›updated_at?: string | undefined;
updated_at?: string | undefined;›archived_at?: string | undefined | undefined;
archived_at?: string | undefined | undefined;›createProject?: (request: CreateActorProjectRequest) => Promise<ApiResult<ActorProject>> | undefined;
createProject?: (request: CreateActorProjectRequest) => Promise<ApiResult<ActorProject>> | undefined;›updateProject?: (projectId: string, request: UpdateActorProjectRequest) => Promise<ApiResult<ActorProject>> | undefined;
updateProject?: (projectId: string, request: UpdateActorProjectRequest) => Promise<ApiResult<ActorProject>> | undefined;›archiveProject?: (projectId: string) => Promise<ApiResult<ActorProject>> | undefined;
archiveProject?: (projectId: string) => Promise<ApiResult<ActorProject>> | undefined;›unarchiveProject?: (projectId: string) => Promise<ApiResult<ActorProject>> | undefined;
unarchiveProject?: (projectId: string) => Promise<ApiResult<ActorProject>> | undefined;useActorProjectSearch()
useActorProjectSearch() is the search-focused project hook. It returns paged project search results for a free-text query and is a better fit for palettes, jump menus, and search boxes than the full project list hook.
import { useState } from 'react';import { useActorProjectSearch } from '@wacht/nextjs';export default function ProjectSearch() { const [query, setQuery] = useState(''); const { projects, loading } = useActorProjectSearch({ query, limit: 12 }); return ( <div> <input value={query} onChange={(event) => setQuery(event.target.value)} /> {loading ? <div>Searching…</div> : null} <ul> {projects.map((project) => ( <li key={project.id}>{project.name}</li> ))} </ul> </div> );}›projects?: ActorProject[] | undefined;
projects?: ActorProject[] | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›hasMore?: boolean | undefined;
hasMore?: boolean | undefined;›nextCursor?: string | undefined | undefined;
nextCursor?: string | undefined | undefined;useActorThreadSearch()
useActorThreadSearch() is the search hook for the actor-wide thread surface. It returns matching threads across projects, which makes it useful for jump menus, global search palettes, and “open thread” UIs.
import { useState } from 'react';import { useActorThreadSearch } from '@wacht/nextjs';export default function ThreadSearch() { const [query, setQuery] = useState(''); const { threads } = useActorThreadSearch({ query, limit: 16 }); return ( <div> <input value={query} onChange={(event) => setQuery(event.target.value)} /> <ul> {threads.map((thread) => ( <li key={thread.id}>{thread.title || thread.responsibility || thread.id}</li> ))} </ul> </div> );}›threads?: AgentThread[] | undefined;
threads?: AgentThread[] | undefined;›hasMore?: boolean | undefined;
hasMore?: boolean | undefined;›nextCursor?: string | undefined | undefined;
nextCursor?: string | undefined | undefined;useProjectThreads()
useProjectThreads() is the project-local thread collection hook. It lists the threads that belong to one project and exposes the thread mutations that create, update, archive, and unarchive those threads.
import { useProjectThreads } from '@wacht/nextjs';export default function ProjectThreadList({ projectId,}: { projectId: string;}) { const { threads, createThread } = useProjectThreads(projectId); return ( <div> <button onClick={async () => { await createThread({ title: 'Planning thread', agent_id: 'agent_123', }); }} > New thread </button> <ul> {threads.map((thread) => ( <li key={thread.id}>{thread.title}</li> ))} </ul> </div> );}›threads?: AgentThread[] | undefined;
threads?: AgentThread[] | undefined;›createThread?: (request: CreateAgentThreadRequest) => Promise<ApiResult<AgentThread>> | undefined;
createThread?: (request: CreateAgentThreadRequest) => Promise<ApiResult<AgentThread>> | undefined;›createThreadForProject?: (targetProjectId: string, request: CreateAgentThreadRequest) => Promise<ApiResult<AgentThread>> | undefined;
createThreadForProject?: (targetProjectId: string, request: CreateAgentThreadRequest) => Promise<ApiResult<AgentThread>> | undefined;›updateThread?: (threadId: string, request: UpdateAgentThreadRequest) => Promise<ApiResult<AgentThread>> | undefined;
updateThread?: (threadId: string, request: UpdateAgentThreadRequest) => Promise<ApiResult<AgentThread>> | undefined;›archiveThread?: (threadId: string) => Promise<ApiResult<AgentThread>> | undefined;
archiveThread?: (threadId: string) => Promise<ApiResult<AgentThread>> | undefined;›unarchiveThread?: (threadId: string) => Promise<ApiResult<AgentThread>> | undefined;
unarchiveThread?: (threadId: string) => Promise<ApiResult<AgentThread>> | undefined;useProjectThreadFeed()
useProjectThreadFeed() is the infinite-list thread feed hook. It returns a flattened thread list together with loadMore() and the archive/query controls that a project sidebar usually needs.
import { useProjectThreadFeed } from '@wacht/nextjs';export default function ThreadFeed({ projectId,}: { projectId: string;}) { const { threads, hasMore, loadMore } = useProjectThreadFeed(projectId, { query: '', }); return ( <div> <ul> {threads.map((thread) => ( <li key={thread.id}>{thread.title}</li> ))} </ul> {hasMore ? <button onClick={() => void loadMore()}>Load more</button> : null} </div> );}›threads?: AgentThread[] | undefined;
threads?: AgentThread[] | undefined;›hasMore?: boolean | undefined;
hasMore?: boolean | undefined;›loadingMore?: boolean | undefined;
loadingMore?: boolean | undefined;›loadMore?: () => Promise<void> | undefined;
loadMore?: () => Promise<void> | undefined;useAgentThread()
useAgentThread() is the single-thread hook. It loads one thread by id and exposes the thread-level update, archive, and unarchive helpers for detail pages and editors.
import { useAgentThread } from '@wacht/nextjs';export default function ThreadDetail({ threadId,}: { threadId: string;}) { const { thread, updateThread } = useAgentThread(threadId, true); if (!thread) { return <div>Loading thread...</div>; } return ( <button onClick={async () => { await updateThread({ title: 'Updated title', }); }} > {thread.title} </button> );}›thread?: AgentThread | null | undefined;
thread?: AgentThread | null | undefined;›id?: string | undefined;
id?: string | undefined;›project_id?: string | undefined;
project_id?: string | undefined;›title?: string | undefined;
title?: string | undefined;›thread_purpose?: string | undefined;
thread_purpose?: string | undefined;›status?: string | undefined;
status?: string | undefined;›execution_state?: ThreadExecutionState | undefined | undefined;
execution_state?: ThreadExecutionState | undefined | undefined;useAgentThreadConversation()
useAgentThreadConversation() is the live conversation hook behind the agent chat surface. It loads message history, subscribes to streaming updates, tracks pending submissions and approvals, and exposes the send, approval, and execution controls that the chat UI needs.
import { useAgentThreadConversation } from '@wacht/nextjs';export default function ThreadConversation({ threadId,}: { threadId: string;}) { const { messages, sendMessage, pendingApprovalRequest, submitApprovalResponse, } = useAgentThreadConversation({ threadId }); return ( <div> <ul> {messages.map((message) => ( <li key={message.id}>{message.content.type}</li> ))} </ul> {pendingApprovalRequest ? ( <button onClick={() => submitApprovalResponse('allow_once', pendingApprovalRequest.request_message_id) } > Approve once </button> ) : null} <button onClick={() => void sendMessage('Continue with the plan')}>Send</button> </div> );}›messages?: ConversationMessage[] | undefined;
messages?: ConversationMessage[] | undefined;›pendingMessage?: string | null | undefined;
pendingMessage?: string | null | undefined;›pendingFiles?: File[] | null | undefined;
pendingFiles?: File[] | null | undefined;›messagesLoading?: boolean | undefined;
messagesLoading?: boolean | undefined;›hasMoreMessages?: boolean | undefined;
hasMoreMessages?: boolean | undefined;›isLoadingMore?: boolean | undefined;
isLoadingMore?: boolean | undefined;›sendMessage?: (message: string, files?: File[]) => Promise<void> | undefined;
sendMessage?: (message: string, files?: File[]) => Promise<void> | undefined;›loadMoreMessages?: () => Promise<void> | undefined;
loadMoreMessages?: () => Promise<void> | undefined;›submitApprovalResponse?: (mode: 'allow_once' | 'allow_always' | 'reject', requestMessageId?: string) => Promise<void> | undefined;
submitApprovalResponse?: (mode: 'allow_once' | 'allow_always' | 'reject', requestMessageId?: string) => Promise<void> | undefined;›cancelExecution?: () => Promise<void> | undefined;
cancelExecution?: () => Promise<void> | undefined;›pendingApprovalRequest?: ThreadPendingApprovalRequestState | null | undefined;
pendingApprovalRequest?: ThreadPendingApprovalRequestState | null | undefined;›activeApprovalRequestId?: string | null | undefined;
activeApprovalRequestId?: string | null | undefined;›hasActiveRun?: boolean | undefined;
hasActiveRun?: boolean | undefined;›isRunning?: boolean | undefined;
isRunning?: boolean | undefined;useAgentThreadFilesystem()
useAgentThreadFilesystem() is the thread workspace file hook. It exposes the top-level listing for the thread filesystem and the helper methods that read individual files or nested directories inside that workspace.
import { useEffect } from 'react';import { useAgentThreadFilesystem } from '@wacht/nextjs';export default function FilesystemPane({ threadId,}: { threadId: string;}) { const { filesystem, getFile } = useAgentThreadFilesystem(threadId, true); useEffect(() => { const firstFile = filesystem.files.find((entry) => !entry.is_dir); if (firstFile) { void getFile(firstFile.path); } }, [filesystem.files, getFile]); return <div>{filesystem.files.length} items</div>;}›filesystem?: ProjectTaskWorkspaceListing | undefined;
filesystem?: ProjectTaskWorkspaceListing | undefined;›exists?: boolean | undefined;
exists?: boolean | undefined;›files?: ProjectTaskWorkspaceFileEntry[] | undefined;
files?: ProjectTaskWorkspaceFileEntry[] | undefined;›getFile?: (path: string) => Promise<ProjectTaskWorkspaceFileContent & { blob: Blob }> | undefined;
getFile?: (path: string) => Promise<ProjectTaskWorkspaceFileContent & { blob: Blob }> | undefined;›listDirectory?: (path?: string) => Promise<ProjectTaskWorkspaceListing> | undefined;
listDirectory?: (path?: string) => Promise<ProjectTaskWorkspaceListing> | undefined;useAgentThreadEvents()
useAgentThreadEvents() reads the paged event feed for one thread. It is the right hook for activity timelines, execution logs, and review surfaces that need the thread event stream but not the full conversation surface.
import { useAgentThreadEvents } from '@wacht/nextjs';export default function ThreadEvents({ threadId,}: { threadId: string;}) { const { events, hasMore, loadMore } = useAgentThreadEvents(threadId, { enabled: !!threadId, }); return ( <div> <ul> {events.map((event) => ( <li key={event.id}>{event.event_type}</li> ))} </ul> {hasMore ? <button onClick={() => void loadMore()}>More</button> : null} </div> );}›events?: ThreadEvent[] | undefined;
events?: ThreadEvent[] | undefined;›hasMore?: boolean | undefined;
hasMore?: boolean | undefined;›loadingMore?: boolean | undefined;
loadingMore?: boolean | undefined;useAgentThreadAssignments()
useAgentThreadAssignments() reads the assignment feed for one thread. It is useful for coordinator and review surfaces where you need the ordered assignment list without the larger task board item surface.
import { useAgentThreadAssignments } from '@wacht/nextjs';export default function ThreadAssignments({ threadId,}: { threadId: string;}) { const { assignments } = useAgentThreadAssignments(threadId, { enabled: !!threadId, }); return ( <ul> {assignments.map((assignment) => ( <li key={assignment.id}>{assignment.assignment_role}</li> ))} </ul> );}›assignments?: ProjectTaskBoardItemAssignment[] | undefined;
assignments?: ProjectTaskBoardItemAssignment[] | undefined;›id?: string | undefined;
id?: string | undefined;›assignment_role?: string | undefined;
assignment_role?: string | undefined;›assignment_order?: number | undefined;
assignment_order?: number | undefined;›status?: string | undefined;
status?: string | undefined;›result_status?: string | undefined | undefined;
result_status?: string | undefined | undefined;useProjectTasks()
useProjectTasks() is the project task board hook. It returns the current task items for one project and exposes the task-level create, archive, and unarchive helpers used by task board screens.
import { useProjectTasks } from '@wacht/nextjs';export default function ProjectTasks({ projectId,}: { projectId: string;}) { const { tasks, createTask } = useProjectTasks(projectId, true, { limit: 40, }); return ( <div> <button onClick={async () => { await createTask({ title: 'Review webhook errors', priority: 'high', }); }} > New task </button> <ul> {tasks.map((task) => ( <li key={task.id}>{task.title}</li> ))} </ul> </div> );}›tasks?: ProjectTaskBoardItem[] | undefined;
tasks?: ProjectTaskBoardItem[] | undefined;›createTask?: (request: CreateProjectTaskBoardItemRequest, files?: File[]) => Promise<ApiResult<ProjectTaskBoardItem>> | undefined;
createTask?: (request: CreateProjectTaskBoardItemRequest, files?: File[]) => Promise<ApiResult<ProjectTaskBoardItem>> | undefined;›archiveTask?: (itemId: string) => Promise<ApiResult<ProjectTaskBoardItem>> | undefined;
archiveTask?: (itemId: string) => Promise<ApiResult<ProjectTaskBoardItem>> | undefined;›unarchiveTask?: (itemId: string) => Promise<ApiResult<ProjectTaskBoardItem>> | undefined;
unarchiveTask?: (itemId: string) => Promise<ApiResult<ProjectTaskBoardItem>> | undefined;useProjectTaskBoardItem()
useProjectTaskBoardItem() is the detail hook for one task board item. It combines the item record, journal events, assignments, and workspace listing so a task detail screen can stay on one hook instead of composing those pieces manually.
import { useProjectTaskBoardItem } from '@wacht/nextjs';export default function TaskDetail({ projectId, itemId,}: { projectId: string; itemId: string;}) { const { item, appendJournal, taskWorkspace } = useProjectTaskBoardItem( projectId, itemId, true, ); return ( <div> <h1>{item?.title}</h1> <p>{taskWorkspace.files.length} workspace entries</p> <button onClick={async () => { await appendJournal({ summary: 'Reviewed task output', }); }} > Add journal entry </button> </div> );}›item?: ProjectTaskBoardItem | null | undefined;
item?: ProjectTaskBoardItem | null | undefined;›events?: ProjectTaskBoardItemEvent[] | undefined;
events?: ProjectTaskBoardItemEvent[] | undefined;›assignments?: ProjectTaskBoardItemAssignment[] | undefined;
assignments?: ProjectTaskBoardItemAssignment[] | undefined;›taskWorkspace?: ProjectTaskWorkspaceListing | undefined;
taskWorkspace?: ProjectTaskWorkspaceListing | undefined;›updateItem?: (request: UpdateProjectTaskBoardItemRequest, files?: File[]) => Promise<ApiResult<ProjectTaskBoardItem>> | undefined;
updateItem?: (request: UpdateProjectTaskBoardItemRequest, files?: File[]) => Promise<ApiResult<ProjectTaskBoardItem>> | undefined;›appendJournal?: (request: AppendProjectTaskBoardItemJournalRequest, files?: File[]) => Promise<ApiResult<ProjectTaskBoardItemEvent>> | undefined;
appendJournal?: (request: AppendProjectTaskBoardItemJournalRequest, files?: File[]) => Promise<ApiResult<ProjectTaskBoardItemEvent>> | undefined;›getTaskWorkspaceFile?: (path: string) => Promise<ApiResult<ProjectTaskWorkspaceFileContent>> | undefined;
getTaskWorkspaceFile?: (path: string) => Promise<ApiResult<ProjectTaskWorkspaceFileContent>> | undefined;useAgentThreadTaskGraphs()
useAgentThreadTaskGraphs() reads the paged task graph bundles for one thread. It is the hook behind task graph drawers and graph timelines, where the UI needs the graph, nodes, edges, and summary together.
import { useAgentThreadTaskGraphs } from '@wacht/nextjs';export default function TaskGraphs({ threadId,}: { threadId: string;}) { const { graphs, latestGraph } = useAgentThreadTaskGraphs(threadId, true); return ( <div> <p>{graphs.length} graphs loaded</p> <p>Latest: {latestGraph?.graph.status ?? 'none'}</p> </div> );}›graphs?: ThreadTaskGraphBundle[] | undefined;
graphs?: ThreadTaskGraphBundle[] | undefined;›graph?: ThreadTaskGraph | undefined;
graph?: ThreadTaskGraph | undefined;›nodes?: ThreadTaskNode[] | undefined;
nodes?: ThreadTaskNode[] | undefined;›edges?: ThreadTaskEdge[] | undefined;
edges?: ThreadTaskEdge[] | undefined;›summary?: ThreadTaskGraphSummary | null | undefined | undefined;
summary?: ThreadTaskGraphSummary | null | undefined | undefined;›latestGraph?: ThreadTaskGraphBundle | null | undefined;
latestGraph?: ThreadTaskGraphBundle | null | undefined;useActorMcpServers()
useActorMcpServers() is the MCP server management hook for the agents surface. It lists the deployment MCP servers available to the current actor and exposes the two connection actions that open or revoke a user connection.
import { useActorMcpServers } from '@wacht/nextjs';export default function McpServers() { const { servers, connect, disconnect } = useActorMcpServers(true); return ( <ul> {servers.map((server) => ( <li key={server.id}> {server.name} <button onClick={async () => { const result = await connect(server.id); window.open(result.data.auth_url, '_blank', 'noopener,noreferrer'); }} > Connect </button> <button onClick={() => void disconnect(server.id)}>Disconnect</button> </li> ))} </ul> );}›servers?: ActorMcpServerSummary[] | undefined;
servers?: ActorMcpServerSummary[] | undefined;›id?: string | undefined;
id?: string | undefined;›name?: string | undefined;
name?: string | undefined;›endpoint?: string | undefined;
endpoint?: string | undefined;›auth_type?: string | undefined;
auth_type?: string | undefined;›requires_user_connection?: boolean | undefined;
requires_user_connection?: boolean | undefined;›connection_status?: "ready" | "connected" | "not_connected" | "expired" | undefined;
connection_status?: "ready" | "connected" | "not_connected" | "expired" | undefined;›connected_at?: string | undefined | undefined;
connected_at?: string | undefined | undefined;›expires_at?: string | undefined | undefined;
expires_at?: string | undefined | undefined;›connect?: (mcpServerId: string) => Promise<ApiResult<{ auth_url: string }>> | undefined;
connect?: (mcpServerId: string) => Promise<ApiResult<{ auth_url: string }>> | undefined;›disconnect?: (mcpServerId: string) => Promise<ApiResult<{ success: boolean }>> | undefined;
disconnect?: (mcpServerId: string) => Promise<ApiResult<{ success: boolean }>> | undefined;Webhooks
useWebhookAppSession()
useWebhookAppSession() is the entry hook for the webhook console. It resolves the current webhook app session, exposes the current webhook app, and bundles the top-level endpoint, settings, replay, and secret-rotation actions that the rest of your webhook UI can share.
import { useWebhookAppSession } from '@wacht/nextjs';export default function WebhookShell({ ticket,}: { ticket?: string | null;}) { const { hasSession, sessionLoading, webhookApp, rotateSecret, } = useWebhookAppSession(ticket); if (sessionLoading) { return <div>Loading webhook app...</div>; } if (!hasSession) { return <div>Access required</div>; } return ( <div> <h1>{webhookApp?.name}</h1> <button onClick={rotateSecret}>Rotate secret</button> </div> );}›ticket?: string | null | undefined | undefined;
ticket?: string | null | undefined | undefined;›hasSession?: boolean | undefined;
hasSession?: boolean | undefined;›sessionLoading?: boolean | undefined;
sessionLoading?: boolean | undefined;›sessionError?: Error | null | undefined;
sessionError?: Error | null | undefined;›sessionId?: string | null | undefined;
sessionId?: string | null | undefined;›webhookApp?: WebhookAppInfo | null | undefined;
webhookApp?: WebhookAppInfo | null | undefined;›app_slug?: string | undefined;
app_slug?: string | undefined;›name?: string | undefined;
name?: string | undefined;›signing_secret?: string | undefined;
signing_secret?: string | undefined;›is_active?: boolean | undefined;
is_active?: boolean | undefined;›failure_notification_emails?: string[] | undefined | undefined;
failure_notification_emails?: string[] | undefined | undefined;›ticketExchanged?: boolean | undefined;
ticketExchanged?: boolean | undefined;›ticketLoading?: boolean | undefined;
ticketLoading?: boolean | undefined;›refetch?: () => Promise<void> | undefined;
refetch?: () => Promise<void> | undefined;›createEndpoint?: (options: CreateEndpointOptions) => Promise<ApiResult<EndpointWithSubscriptions>> | undefined;
createEndpoint?: (options: CreateEndpointOptions) => Promise<ApiResult<EndpointWithSubscriptions>> | undefined;›updateEndpoint?: (endpointId: string, options: UpdateEndpointOptions) => Promise<ApiResult<EndpointWithSubscriptions>> | undefined;
updateEndpoint?: (endpointId: string, options: UpdateEndpointOptions) => Promise<ApiResult<EndpointWithSubscriptions>> | undefined;›deleteEndpoint?: (endpointId: string) => Promise<ApiResult<DeleteEndpointResponse>> | undefined;
deleteEndpoint?: (endpointId: string) => Promise<ApiResult<DeleteEndpointResponse>> | undefined;›testEndpoint?: (endpointId: string, options: TestEndpointOptions) => Promise<ApiResult<TestEndpointResponse>> | undefined;
testEndpoint?: (endpointId: string, options: TestEndpointOptions) => Promise<ApiResult<TestEndpointResponse>> | undefined;›rotateSecret?: () => Promise<ApiResult<WebhookAppInfo>> | undefined;
rotateSecret?: () => Promise<ApiResult<WebhookAppInfo>> | undefined;›updateSettings?: (options: UpdateWebhookSettingsOptions) => Promise<ApiResult<WebhookSettingsResponse>> | undefined;
updateSettings?: (options: UpdateWebhookSettingsOptions) => Promise<ApiResult<WebhookSettingsResponse>> | undefined;›replayDelivery?: (options: ReplayWebhookDeliveryOptions) => Promise<ApiResult<ReplayWebhookDeliveryResponse>> | undefined;
replayDelivery?: (options: ReplayWebhookDeliveryOptions) => Promise<ApiResult<ReplayWebhookDeliveryResponse>> | undefined;›fetchReplayTaskStatus?: (options: ReplayTaskStatusOptions) => Promise<ApiResult<ReplayTaskStatusResponse>> | undefined;
fetchReplayTaskStatus?: (options: ReplayTaskStatusOptions) => Promise<ApiResult<ReplayTaskStatusResponse>> | undefined;›fetchReplayTasks?: (options?: ReplayTaskListOptions) => Promise<ApiResult<ReplayTaskListResponse>> | undefined;
fetchReplayTasks?: (options?: ReplayTaskListOptions) => Promise<ApiResult<ReplayTaskListResponse>> | undefined;›cancelReplayTask?: (options: CancelReplayTaskOptions) => Promise<ApiResult<CancelReplayTaskResponse>> | undefined;
cancelReplayTask?: (options: CancelReplayTaskOptions) => Promise<ApiResult<CancelReplayTaskResponse>> | undefined;›fetchDeliveryDetail?: (deliveryId: string) => Promise<ApiResult<WebhookDeliveryDetail[]>> | undefined;
fetchDeliveryDetail?: (deliveryId: string) => Promise<ApiResult<WebhookDeliveryDetail[]>> | undefined;useWebhookStats()
useWebhookStats() is the small summary hook for the webhook surface. It returns the top-level counts for endpoints, events, and pending deliveries, which makes it a good fit for dashboard cards, header metrics, and other compact overview UI.
import { useWebhookStats } from '@wacht/nextjs';export default function WebhookSnapshot() { const { stats, loading } = useWebhookStats(); if (loading) { return <div>Loading stats...</div>; } return ( <div> <p>Endpoints: {stats?.endpoint_count ?? 0}</p> <p>Events: {stats?.event_count ?? 0}</p> <p>Pending deliveries: {stats?.pending_deliveries ?? 0}</p> </div> );}›stats?: WebhookStats | null | undefined;
stats?: WebhookStats | null | undefined;›endpoint_count?: number | undefined;
endpoint_count?: number | undefined;›event_count?: number | undefined;
event_count?: number | undefined;›pending_deliveries?: number | undefined;
pending_deliveries?: number | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›error?: unknown | undefined;
error?: unknown | undefined;›refetch?: () => void | undefined;
refetch?: () => void | undefined;useWebhookEndpoints()
useWebhookEndpoints() is the main list hook for the webhook endpoints surface. It returns the current endpoint list with subscriptions attached, which makes it the natural source for endpoint tables, endpoint pickers, and navigation into endpoint detail pages.
import { useWebhookEndpoints } from '@wacht/nextjs';export default function EndpointList() { const { endpoints, loading } = useWebhookEndpoints(); if (loading) { return <div>Loading endpoints...</div>; } return ( <ul> {endpoints.map((endpoint) => ( <li key={endpoint.id}> {endpoint.url} · {endpoint.subscribed_events.length} events </li> ))} </ul> );}›endpoints?: EndpointWithSubscriptions[] | undefined;
endpoints?: EndpointWithSubscriptions[] | undefined;›id?: string | undefined;
id?: string | undefined;›deployment_id?: string | undefined;
deployment_id?: string | undefined;›app_slug?: string | undefined;
app_slug?: string | undefined;›url?: string | undefined;
url?: string | undefined;›description?: string | undefined | undefined;
description?: string | undefined | undefined;›headers?: Record<string, string> | undefined | undefined;
headers?: Record<string, string> | undefined | undefined;›is_active?: boolean | undefined;
is_active?: boolean | undefined;›max_retries?: number | undefined;
max_retries?: number | undefined;›timeout_seconds?: number | undefined;
timeout_seconds?: number | undefined;›failure_count?: number | undefined;
failure_count?: number | undefined;›last_failure_at?: string | undefined | undefined;
last_failure_at?: string | undefined | undefined;›auto_disabled?: boolean | undefined;
auto_disabled?: boolean | undefined;›auto_disabled_at?: string | undefined | undefined;
auto_disabled_at?: string | undefined | undefined;›rate_limit_config?: RateLimitConfig | null | undefined | undefined;
rate_limit_config?: RateLimitConfig | null | undefined | undefined;›duration_ms?: number | undefined;
duration_ms?: number | undefined;›max_requests?: number | undefined;
max_requests?: number | undefined;›created_at?: string | undefined;
created_at?: string | undefined;›updated_at?: string | undefined;
updated_at?: string | undefined;›subscribed_events?: string[] | undefined;
subscribed_events?: string[] | undefined;›subscriptions?: WebhookEventSubscription[] | undefined;
subscriptions?: WebhookEventSubscription[] | undefined;›event_name?: string | undefined;
event_name?: string | undefined;›filter_rules?: Record<string, unknown> | undefined | undefined;
filter_rules?: Record<string, unknown> | undefined | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›error?: unknown | undefined;
error?: unknown | undefined;›refetch?: () => void | undefined;
refetch?: () => void | undefined;useCreateWebhookEndpoint()
useCreateWebhookEndpoint() is the narrow write hook for creating a webhook endpoint. It is useful when your screen only needs endpoint creation and does not need the broader webhook app session helpers from useWebhookAppSession().
import { useCreateWebhookEndpoint } from '@wacht/nextjs';export default function CreateEndpointButton() { const { createEndpoint } = useCreateWebhookEndpoint(); async function handleCreate() { await createEndpoint({ url: 'https://example.com/webhooks', subscribed_events: ['user.created'], }); } return <button onClick={handleCreate}>Create endpoint</button>;}›createEndpoint?: (options: CreateEndpointOptions) => Promise<ApiResult<EndpointWithSubscriptions>> | undefined;
createEndpoint?: (options: CreateEndpointOptions) => Promise<ApiResult<EndpointWithSubscriptions>> | undefined;›options.url?: string | undefined;
options.url?: string | undefined;›options.description?: string | undefined | undefined;
options.description?: string | undefined | undefined;›options.subscribed_events?: string[] | undefined;
options.subscribed_events?: string[] | undefined;›options.subscriptions?: WebhookEventSubscription[] | undefined | undefined;
options.subscriptions?: WebhookEventSubscription[] | undefined | undefined;›event_name?: string | undefined;
event_name?: string | undefined;›filter_rules?: Record<string, unknown> | undefined | undefined;
filter_rules?: Record<string, unknown> | undefined | undefined;›options.headers?: Record<string, string> | undefined | undefined;
options.headers?: Record<string, string> | undefined | undefined;›options.rate_limit_config?: RateLimitConfig | null | undefined | undefined;
options.rate_limit_config?: RateLimitConfig | null | undefined | undefined;›duration_ms?: number | undefined;
duration_ms?: number | undefined;›max_requests?: number | undefined;
max_requests?: number | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›error?: unknown | undefined;
error?: unknown | undefined;useWebhookDeliveries()
useWebhookDeliveries() is the main operational list hook for webhook traffic. It returns delivery rows for the current app, with optional filters for endpoint, event, status, and cursor position, which makes it the hook you use for webhook logs, endpoint delivery views, and replay selection surfaces.
import { useState } from 'react';import { useWebhookDeliveries } from '@wacht/nextjs';export default function WebhookLogsPage() { const [status, setStatus] = useState<string>('all'); const [eventName, setEventName] = useState<string>('all'); const { deliveries, has_more, next_cursor, loading } = useWebhookDeliveries({ status: status === 'all' ? undefined : status, event_name: eventName === 'all' ? undefined : eventName, limit: 30, }); if (loading) { return <div>Loading deliveries...</div>; } return ( <div> <p>{deliveries.length} deliveries</p> <p>{has_more ? next_cursor : 'No more pages'}</p> </div> );}›options?: UseWebhookDeliveriesOptions | undefined | undefined;
options?: UseWebhookDeliveriesOptions | undefined | undefined;›endpoint_id?: string | undefined | undefined;
endpoint_id?: string | undefined | undefined;›status?: string | undefined | undefined;
status?: string | undefined | undefined;›event_name?: string | undefined | undefined;
event_name?: string | undefined | undefined;›limit?: number | undefined | undefined;
limit?: number | undefined | undefined;›cursor?: string | undefined | undefined;
cursor?: string | undefined | undefined;›deliveries?: WebhookDelivery[] | undefined;
deliveries?: WebhookDelivery[] | undefined;›id?: string | undefined;
id?: string | undefined;›deployment_id?: string | undefined;
deployment_id?: string | undefined;›app_slug?: string | undefined;
app_slug?: string | undefined;›endpoint_id?: string | undefined;
endpoint_id?: string | undefined;›event_name?: string | undefined;
event_name?: string | undefined;›event_type?: string | undefined;
event_type?: string | undefined;›status?: string | undefined;
status?: string | undefined;›http_status_code?: number | undefined | undefined;
http_status_code?: number | undefined | undefined;›response_status?: number | undefined | undefined;
response_status?: number | undefined | undefined;›response_time_ms?: number | undefined | undefined;
response_time_ms?: number | undefined | undefined;›attempt_number?: number | undefined;
attempt_number?: number | undefined;›max_attempts?: number | undefined;
max_attempts?: number | undefined;›timestamp?: string | undefined;
timestamp?: string | undefined;›created_at?: string | undefined;
created_at?: string | undefined;›has_more?: boolean | undefined;
has_more?: boolean | undefined;›next_cursor?: string | undefined | undefined;
next_cursor?: string | undefined | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›error?: unknown | undefined;
error?: unknown | undefined;›refetch?: () => void | undefined;
refetch?: () => void | undefined;useWebhookEvents()
useWebhookEvents() returns the event catalog for the current webhook app. It is the hook you use when you want to show the available event names, descriptions, schema metadata, or example payloads in your own webhook console.
import { useWebhookEvents } from '@wacht/nextjs';export default function EventCatalog() { const { events, loading } = useWebhookEvents(); if (loading) { return <div>Loading events...</div>; } return ( <ul> {events.map((event) => ( <li key={event.event_name}> {event.event_name} </li> ))} </ul> );}›events?: WebhookAppEvent[] | undefined;
events?: WebhookAppEvent[] | undefined;›deployment_id?: string | undefined;
deployment_id?: string | undefined;›app_slug?: string | undefined;
app_slug?: string | undefined;›event_name?: string | undefined;
event_name?: string | undefined;›group?: string | undefined | undefined;
group?: string | undefined | undefined;›description?: string | undefined | undefined;
description?: string | undefined | undefined;›schema?: Record<string, unknown> | undefined | undefined;
schema?: Record<string, unknown> | undefined | undefined;›example_payload?: Record<string, unknown> | undefined | undefined;
example_payload?: Record<string, unknown> | undefined | undefined;›is_archived?: boolean | undefined;
is_archived?: boolean | undefined;›created_at?: string | undefined;
created_at?: string | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›error?: unknown | undefined;
error?: unknown | undefined;›refetch?: () => void | undefined;
refetch?: () => void | undefined;useWebhookAnalytics()
useWebhookAnalytics() returns aggregate webhook metrics for a selected time window. It is the hook you use for overview cards, operational summaries, and endpoint-level performance snapshots where you need rolled-up counts and latency metrics instead of raw delivery rows.
import { useWebhookAnalytics } from '@wacht/nextjs';export default function WebhookOverview() { const { analytics, loading } = useWebhookAnalytics({ fields: ['total_deliveries', 'successful', 'failed', 'success_rate'], }); if (loading) { return <div>Loading analytics...</div>; } return ( <div> <p>Total deliveries: {analytics?.total_deliveries ?? 0}</p> <p>Success rate: {(analytics?.success_rate ?? 0).toFixed(1)}%</p> </div> );}›options?: UseWebhookAnalyticsOptions | undefined | undefined;
options?: UseWebhookAnalyticsOptions | undefined | undefined;›start_date?: string | undefined | undefined;
start_date?: string | undefined | undefined;›end_date?: string | undefined | undefined;
end_date?: string | undefined | undefined;›endpoint_id?: string | undefined | undefined;
endpoint_id?: string | undefined | undefined;›fields?: string[] | undefined | undefined;
fields?: string[] | undefined | undefined;›analytics?: WebhookAnalyticsResponse | null | undefined;
analytics?: WebhookAnalyticsResponse | null | undefined;›total_deliveries?: number | undefined;
total_deliveries?: number | undefined;›total_events?: number | undefined;
total_events?: number | undefined;›successful?: number | undefined;
successful?: number | undefined;›failed?: number | undefined;
failed?: number | undefined;›filtered?: number | undefined;
filtered?: number | undefined;›success_rate?: number | undefined;
success_rate?: number | undefined;›avg_response_time_ms?: number | undefined | undefined;
avg_response_time_ms?: number | undefined | undefined;›p50_response_time_ms?: number | undefined | undefined;
p50_response_time_ms?: number | undefined | undefined;›p95_response_time_ms?: number | undefined | undefined;
p95_response_time_ms?: number | undefined | undefined;›p99_response_time_ms?: number | undefined | undefined;
p99_response_time_ms?: number | undefined | undefined;›avg_payload_size?: number | undefined | undefined;
avg_payload_size?: number | undefined | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›error?: unknown | undefined;
error?: unknown | undefined;›refetch?: () => void | undefined;
refetch?: () => void | undefined;useWebhookTimeseries()
useWebhookTimeseries() returns bucketed delivery metrics for a selected time range. It is the hook you use for webhook charts, trend views, and endpoint-level time-based dashboards where the UI needs counts grouped by hour or day rather than one aggregate snapshot.
import { useWebhookTimeseries } from '@wacht/nextjs';export default function DeliveryTrend({ startDate, endDate,}: { startDate: string; endDate: string;}) { const { timeseries, loading } = useWebhookTimeseries({ start_date: startDate, end_date: endDate, interval: 'day', }); if (loading) { return <div>Loading chart data...</div>; } return <div>{timeseries.length} points</div>;}›options?: UseWebhookTimeseriesOptions | undefined | undefined;
options?: UseWebhookTimeseriesOptions | undefined | undefined;›start_date?: string | undefined | undefined;
start_date?: string | undefined | undefined;›end_date?: string | undefined | undefined;
end_date?: string | undefined | undefined;›interval?: string | undefined | undefined;
interval?: string | undefined | undefined;›endpoint_id?: string | undefined | undefined;
endpoint_id?: string | undefined | undefined;›timeseries?: TimeseriesPoint[] | undefined;
timeseries?: TimeseriesPoint[] | undefined;›timestamp?: string | undefined;
timestamp?: string | undefined;›total_events?: number | undefined;
total_events?: number | undefined;›total_deliveries?: number | undefined;
total_deliveries?: number | undefined;›successful_deliveries?: number | undefined;
successful_deliveries?: number | undefined;›failed_deliveries?: number | undefined;
failed_deliveries?: number | undefined;›filtered_deliveries?: number | undefined;
filtered_deliveries?: number | undefined;›avg_response_time_ms?: number | undefined | undefined;
avg_response_time_ms?: number | undefined | undefined;›success_rate?: number | undefined;
success_rate?: number | undefined;›interval?: string | undefined;
interval?: string | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›error?: unknown | undefined;
error?: unknown | undefined;›refetch?: () => void | undefined;
refetch?: () => void | undefined;API Identity
useApiAuthAppSession()
useApiAuthAppSession() is the entry hook for the API identity surface. It exchanges an optional ticket, loads the current API auth app session, and returns the app metadata that the vanity API auth provider uses to gate the overview, keys, and access-log pages.
import { useApiAuthAppSession } from '@wacht/nextjs';export default function ApiIdentityShell({ ticket,}: { ticket?: string | null;}) { const { hasSession, sessionLoading, apiAuthApp, } = useApiAuthAppSession(ticket); if (sessionLoading) { return <div>Loading API identity...</div>; } if (!hasSession) { return <div>Access required</div>; } return ( <div> <h1>{apiAuthApp?.name}</h1> <p>{apiAuthApp?.key_prefix}</p> </div> );}›ticket?: string | null | undefined | undefined;
ticket?: string | null | undefined | undefined;›hasSession?: boolean | undefined;
hasSession?: boolean | undefined;›sessionLoading?: boolean | undefined;
sessionLoading?: boolean | undefined;›sessionError?: Error | null | undefined;
sessionError?: Error | null | undefined;›sessionId?: string | null | undefined;
sessionId?: string | null | undefined;›apiAuthApp?: ApiAuthAppInfo | null | undefined;
apiAuthApp?: ApiAuthAppInfo | null | undefined;›id?: string | undefined;
id?: string | undefined;›app_slug?: string | undefined;
app_slug?: string | undefined;›name?: string | undefined;
name?: string | undefined;›key_prefix?: string | undefined;
key_prefix?: string | undefined;›description?: string | undefined | undefined;
description?: string | undefined | undefined;›is_active?: boolean | undefined;
is_active?: boolean | undefined;›rate_limits?: RateLimit[] | undefined;
rate_limits?: RateLimit[] | undefined;›unit?: 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'calendar_day' | 'month' | 'calendar_month' | undefined;
unit?: 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'calendar_day' | 'month' | 'calendar_month' | undefined;›duration?: number | undefined;
duration?: number | undefined;›max_requests?: number | undefined;
max_requests?: number | undefined;›mode?: 'per_app' | 'per_key' | 'per_key_and_ip' | 'per_app_and_ip' | undefined | undefined;
mode?: 'per_app' | 'per_key' | 'per_key_and_ip' | 'per_app_and_ip' | undefined | undefined;›ticketExchanged?: boolean | undefined;
ticketExchanged?: boolean | undefined;›ticketLoading?: boolean | undefined;
ticketLoading?: boolean | undefined;›refetch?: () => Promise<void> | undefined;
refetch?: () => Promise<void> | undefined;useApiAuthKeys()
useApiAuthKeys() is the main key-management hook for the API identity surface. It lists keys for the current app, lets you filter by status, and exposes the three lifecycle mutations that the vanity keys page uses directly: create, rotate, and revoke.
import { useState } from 'react';import { useApiAuthKeys } from '@wacht/nextjs';export default function KeysPage() { const [status, setStatus] = useState<'active' | 'revoked' | 'all'>('active'); const { keys, loading, createKey, rotateKey, revokeKey } = useApiAuthKeys({ status, }); if (loading) { return <div>Loading keys...</div>; } return ( <div> <button onClick={async () => { const result = await createKey({ name: 'Production Backend' }); console.log(result.data.secret); }} > Create key </button> <ul> {keys.map((key) => ( <li key={key.id}> {key.name} <button onClick={async () => { const result = await rotateKey({ key_id: key.id }); console.log(result.data.secret); }}>Rotate</button> <button onClick={async () => { await revokeKey({ key_id: key.id }); }}>Revoke</button> </li> ))} </ul> </div> );}›filters?: UseApiAuthKeysFilters | undefined | undefined;
filters?: UseApiAuthKeysFilters | undefined | undefined;›status?: 'active' | 'revoked' | 'all' | undefined | undefined;
status?: 'active' | 'revoked' | 'all' | undefined | undefined;›keys?: ApiKey[] | undefined;
keys?: ApiKey[] | undefined;›id?: string | undefined;
id?: string | undefined;›name?: string | undefined;
name?: string | undefined;›key_prefix?: string | undefined;
key_prefix?: string | undefined;›key_suffix?: string | undefined;
key_suffix?: string | undefined;›permissions?: string[] | undefined;
permissions?: string[] | undefined;›org_role_permissions?: string[] | undefined;
org_role_permissions?: string[] | undefined;›workspace_role_permissions?: string[] | undefined;
workspace_role_permissions?: string[] | undefined;›organization_id?: string | undefined | undefined;
organization_id?: string | undefined | undefined;›workspace_id?: string | undefined | undefined;
workspace_id?: string | undefined | undefined;›organization_membership_id?: string | undefined | undefined;
organization_membership_id?: string | undefined | undefined;›workspace_membership_id?: string | undefined | undefined;
workspace_membership_id?: string | undefined | undefined;›expires_at?: string | undefined | undefined;
expires_at?: string | undefined | undefined;›last_used_at?: string | undefined | undefined;
last_used_at?: string | undefined | undefined;›is_active?: boolean | undefined;
is_active?: boolean | undefined;›created_at?: string | undefined;
created_at?: string | undefined;›revoked_at?: string | undefined | undefined;
revoked_at?: string | undefined | undefined;›revoked_reason?: string | undefined | undefined;
revoked_reason?: string | undefined | undefined;›createKey?: (input: CreateApiAuthKeyInput) => Promise<ApiResult<ApiKeyWithSecret>> | undefined;
createKey?: (input: CreateApiAuthKeyInput) => Promise<ApiResult<ApiKeyWithSecret>> | undefined;›input.name?: string | undefined;
input.name?: string | undefined;›input.expires_at?: string | undefined | undefined;
input.expires_at?: string | undefined | undefined;›result.data.id?: string | undefined;
result.data.id?: string | undefined;›result.data.name?: string | undefined;
result.data.name?: string | undefined;›result.data.key_prefix?: string | undefined;
result.data.key_prefix?: string | undefined;›result.data.key_suffix?: string | undefined;
result.data.key_suffix?: string | undefined;›result.data.permissions?: string[] | undefined;
result.data.permissions?: string[] | undefined;›result.data.expires_at?: string | undefined | undefined;
result.data.expires_at?: string | undefined | undefined;›result.data.last_used_at?: string | undefined | undefined;
result.data.last_used_at?: string | undefined | undefined;›result.data.is_active?: boolean | undefined;
result.data.is_active?: boolean | undefined;›result.data.created_at?: string | undefined;
result.data.created_at?: string | undefined;›result.data.revoked_at?: string | undefined | undefined;
result.data.revoked_at?: string | undefined | undefined;›result.data.revoked_reason?: string | undefined | undefined;
result.data.revoked_reason?: string | undefined | undefined;›result.data.secret?: string | undefined;
result.data.secret?: string | undefined;›rotateKey?: (input: RotateApiAuthKeyInput) => Promise<ApiResult<ApiKeyWithSecret>> | undefined;
rotateKey?: (input: RotateApiAuthKeyInput) => Promise<ApiResult<ApiKeyWithSecret>> | undefined;›input.key_id?: string | undefined;
input.key_id?: string | undefined;›result.data.secret?: string | undefined;
result.data.secret?: string | undefined;›revokeKey?: (input: RevokeApiAuthKeyInput) => Promise<ApiResult<void>> | undefined;
revokeKey?: (input: RevokeApiAuthKeyInput) => Promise<ApiResult<void>> | undefined;›input.key_id?: string | undefined;
input.key_id?: string | undefined;›input.reason?: string | undefined | undefined;
input.reason?: string | undefined | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›error?: unknown | undefined;
error?: unknown | undefined;›refetch?: () => void | undefined;
refetch?: () => void | undefined;useApiAuthAuditLogs()
useApiAuthAuditLogs() is the paginated log hook behind the API identity access-logs page. It returns one page of audit records at a time, plus the paging metadata that the vanity logs view uses to move forward with a cursor and rebuild the query when filters change.
import { useMemo, useState } from 'react';import { addDays } from 'date-fns';import { useApiAuthAuditLogs } from '@wacht/nextjs';export default function AccessLogsPage() { const [page, setPage] = useState(1); const [cursors, setCursors] = useState<string[]>([]); const [outcome, setOutcome] = useState<'all' | 'allowed' | 'blocked'>('all'); const startDate = useMemo(() => { const d = addDays(new Date(), -7); d.setHours(0, 0, 0, 0); return d.toISOString(); }, []); const endDate = useMemo(() => { const d = new Date(); d.setHours(23, 59, 59, 999); return d.toISOString(); }, []); const cursor = page > 1 ? cursors[page - 2] : undefined; const { logs, has_more, next_cursor, loading } = useApiAuthAuditLogs({ limit: 25, cursor, outcome: outcome === 'all' ? undefined : outcome, start_date: startDate, end_date: endDate, }); if (!loading && next_cursor && page === cursors.length + 1) { setCursors((prev) => [...prev, next_cursor]); } return ( <div> <button onClick={() => setOutcome('blocked')}>Blocked only</button> <ul> {logs.map((log) => ( <li key={log.request_id}>{log.path}</li> ))} </ul> <button disabled={!has_more} onClick={() => setPage((current) => current + 1)}> Next </button> </div> );}›options?: UseApiAuthAuditLogsOptions | undefined | undefined;
options?: UseApiAuthAuditLogsOptions | undefined | undefined;›limit?: number | undefined | undefined;
limit?: number | undefined | undefined;›cursor?: string | undefined | undefined;
cursor?: string | undefined | undefined;›outcome?: 'allowed' | 'blocked' | undefined | undefined;
outcome?: 'allowed' | 'blocked' | undefined | undefined;›key_id?: string | undefined | undefined;
key_id?: string | undefined | undefined;›start_date?: string | undefined | undefined;
start_date?: string | undefined | undefined;›end_date?: string | undefined | undefined;
end_date?: string | undefined | undefined;›logs?: ApiAuditLog[] | undefined;
logs?: ApiAuditLog[] | undefined;›request_id?: string | undefined;
request_id?: string | undefined;›deployment_id?: number | undefined;
deployment_id?: number | undefined;›app_slug?: string | undefined;
app_slug?: string | undefined;›key_id?: number | undefined;
key_id?: number | undefined;›key_name?: string | undefined;
key_name?: string | undefined;›outcome?: string | undefined;
outcome?: string | undefined;›blocked_by_rule?: string | undefined | undefined;
blocked_by_rule?: string | undefined | undefined;›client_ip?: string | undefined;
client_ip?: string | undefined;›path?: string | undefined;
path?: string | undefined;›user_agent?: string | undefined;
user_agent?: string | undefined;›rate_limits?: string | undefined | undefined;
rate_limits?: string | undefined | undefined;›timestamp?: string | undefined;
timestamp?: string | undefined;›limit?: number | undefined;
limit?: number | undefined;›has_more?: boolean | undefined;
has_more?: boolean | undefined;›next_cursor?: string | undefined | undefined;
next_cursor?: string | undefined | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›error?: unknown | undefined;
error?: unknown | undefined;›refetch?: () => void | undefined;
refetch?: () => void | undefined;useApiAuthAuditAnalytics()
useApiAuthAuditAnalytics() is the overview analytics hook for API identity. It returns aggregate request counts and optional breakdown sections for keys, paths, blocked reasons, and rate-limit rules. In vanity-pages, the API identity overview uses it alongside useApiAuthAuditTimeseries() to power the top metrics and breakdown cards.
import { addDays } from 'date-fns';import { useApiAuthAuditAnalytics } from '@wacht/nextjs';export default function ApiIdentityOverview() { const start = addDays(new Date(), -7).toISOString(); const end = new Date().toISOString(); const { analytics, loading } = useApiAuthAuditAnalytics({ start_date: start, end_date: end, include_top_keys: true, include_top_paths: true, include_blocked_reasons: true, include_rate_limits: true, }); if (loading) { return <div>Loading analytics...</div>; } return ( <div> <p>Total requests: {analytics?.total_requests}</p> <p>Success rate: {analytics?.success_rate}</p> </div> );}›options?: UseApiAuthAuditAnalyticsOptions | undefined | undefined;
options?: UseApiAuthAuditAnalyticsOptions | undefined | undefined;›start_date?: string | undefined | undefined;
start_date?: string | undefined | undefined;›end_date?: string | undefined | undefined;
end_date?: string | undefined | undefined;›key_id?: string | undefined | undefined;
key_id?: string | undefined | undefined;›include_top_keys?: boolean | undefined | undefined;
include_top_keys?: boolean | undefined | undefined;›include_top_paths?: boolean | undefined | undefined;
include_top_paths?: boolean | undefined | undefined;›include_blocked_reasons?: boolean | undefined | undefined;
include_blocked_reasons?: boolean | undefined | undefined;›include_rate_limits?: boolean | undefined | undefined;
include_rate_limits?: boolean | undefined | undefined;›top_limit?: number | undefined | undefined;
top_limit?: number | undefined | undefined;›analytics?: ApiAuditAnalyticsResponse | null | undefined;
analytics?: ApiAuditAnalyticsResponse | null | undefined;›total_requests?: number | undefined;
total_requests?: number | undefined;›allowed_requests?: number | undefined;
allowed_requests?: number | undefined;›blocked_requests?: number | undefined;
blocked_requests?: number | undefined;›success_rate?: number | undefined;
success_rate?: number | undefined;›keys_used_24h?: number | undefined;
keys_used_24h?: number | undefined;›top_keys?: KeyStatsItem[] | undefined | undefined;
top_keys?: KeyStatsItem[] | undefined | undefined;›key_id?: number | undefined;
key_id?: number | undefined;›key_name?: string | undefined;
key_name?: string | undefined;›total_requests?: number | undefined;
total_requests?: number | undefined;›top_paths?: PathStatsItem[] | undefined | undefined;
top_paths?: PathStatsItem[] | undefined | undefined;›path?: string | undefined;
path?: string | undefined;›total_requests?: number | undefined;
total_requests?: number | undefined;›blocked_reasons?: BlockedReasonItem[] | undefined | undefined;
blocked_reasons?: BlockedReasonItem[] | undefined | undefined;›blocked_by_rule?: string | undefined;
blocked_by_rule?: string | undefined;›count?: number | undefined;
count?: number | undefined;›percentage?: number | undefined;
percentage?: number | undefined;›rate_limit_stats?: RateLimitBreakdown | undefined | undefined;
rate_limit_stats?: RateLimitBreakdown | undefined | undefined;›total_hits?: number | undefined;
total_hits?: number | undefined;›percentage_of_blocked?: number | undefined;
percentage_of_blocked?: number | undefined;›top_rules?: RateLimitStatsItem[] | undefined | undefined;
top_rules?: RateLimitStatsItem[] | undefined | undefined;›rule?: string | undefined;
rule?: string | undefined;›hit_count?: number | undefined;
hit_count?: number | undefined;›percentage?: number | undefined;
percentage?: number | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›error?: unknown | undefined;
error?: unknown | undefined;›refetch?: () => void | undefined;
refetch?: () => void | undefined;useApiAuthAuditTimeseries()
useApiAuthAuditTimeseries() returns the time-bucketed request series for the API identity overview charts. It is the hook that vanity-pages uses to build the request activity graph, and it pairs naturally with useApiAuthAuditAnalytics() when you need both the aggregate metrics and the per-interval chart data for the same date range.
import { addDays } from 'date-fns';import { useApiAuthAuditTimeseries } from '@wacht/nextjs';export default function ApiIdentityChart() { const start = addDays(new Date(), -7).toISOString(); const end = new Date().toISOString(); const { timeseries, interval, loading } = useApiAuthAuditTimeseries({ start_date: start, end_date: end, interval: 'day', }); if (loading) { return <div>Loading chart…</div>; } return ( <div> <p>Interval: {interval}</p> <p>Points: {timeseries.length}</p> </div> );}›options?: UseApiAuthAuditTimeseriesOptions | undefined | undefined;
options?: UseApiAuthAuditTimeseriesOptions | undefined | undefined;›start_date?: string | undefined | undefined;
start_date?: string | undefined | undefined;›end_date?: string | undefined | undefined;
end_date?: string | undefined | undefined;›interval?: string | undefined | undefined;
interval?: string | undefined | undefined;›key_id?: string | undefined | undefined;
key_id?: string | undefined | undefined;›timeseries?: ApiAuditTimeseriesPoint[] | undefined;
timeseries?: ApiAuditTimeseriesPoint[] | undefined;›timestamp?: string | undefined;
timestamp?: string | undefined;›total_requests?: number | undefined;
total_requests?: number | undefined;›allowed_requests?: number | undefined;
allowed_requests?: number | undefined;›blocked_requests?: number | undefined;
blocked_requests?: number | undefined;›success_rate?: number | undefined;
success_rate?: number | undefined;›interval?: string | undefined;
interval?: string | undefined;›loading?: boolean | undefined;
loading?: boolean | undefined;›error?: unknown | undefined;
error?: unknown | undefined;›refetch?: () => void | undefined;
refetch?: () => void | undefined;