---
title: Gateway React API
description: React hooks for driving a Smithers Gateway from a custom workflow UI.
icon: "react"
---

The Smithers UI surface is React. This package is the SDK that backs it: a
provider that holds one `SmithersGatewayClient`, a set of live data hooks that
read a run (or runs, approvals, crons, scores, …) over TanStack DB collections,
and action hooks that launch, approve, and cancel. Most hooks return the same
[`GatewayAsyncState`](#gatewayasyncstate) shape, so once you know one you know
them all.

<Note>
Everything on this page lives at the subpath `smithers-orchestrator/gateway-react`,
**not** on the bare `smithers-orchestrator` facade. Import from the subpath or
the build fails.
</Note>

```tsx
import {
  SmithersGatewayProvider,
  useGatewayRun,
  useGatewayActions,
} from "smithers-orchestrator/gateway-react";

function App() {
  return (
    <SmithersGatewayProvider options={{ baseUrl: "/" }}>
      <Run runId={RUN_ID} />
    </SmithersGatewayProvider>
  );
}

function Run({ runId }: { runId: string }) {
  const { data, loading, error } = useGatewayRun(runId);
  const { launchRun, submitApproval } = useGatewayActions();
  if (loading) return <p>loading…</p>;
  if (error) return <p>{error.message}</p>;
  return <pre>{String(data?.status ?? "")}</pre>;
}
```

For a UI bundled and served by the gateway itself, skip the JSX provider and
mount with [`createGatewayReactRoot`](#creategatewayreactroot) instead. It wires
both contexts (on-demand hooks **and** the live collections) in one call.

## GatewayAsyncState

The return shape shared by the live data hooks. `data` is `undefined` until the
first payload lands; `loading` is true only while there is no data yet and a
fetch is in flight; `refetch()` forces a re-pull.

```ts
type GatewayAsyncState<T> = {
  data: T | undefined;
  error: Error | undefined;
  loading: boolean;
  refetch: () => Promise<void>;
};
```

<ResponseField name="GatewayAsyncState<T>" type="object">
  <Expandable title="members">
    <ResponseField name="data" type="T | undefined">
      The latest payload, or `undefined` before the first fetch resolves.
    </ResponseField>
    <ResponseField name="error" type="Error | undefined">
      The last fetch error, if any. Live collection hooks resolve via stream and
      generally leave this `undefined`.
    </ResponseField>
    <ResponseField name="loading" type="boolean">
      True only when there is no `data` yet and a fetch is in flight.
    </ResponseField>
    <ResponseField name="refetch" type="() => Promise<void>">
      Invalidate the backing collection and re-pull.
    </ResponseField>
  </Expandable>
</ResponseField>

**Source** [`GatewayAsyncState.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/GatewayAsyncState.ts) · **See also** [Custom workflow UI](/guides/custom-workflow-ui)

## Provider & root

### SmithersGatewayProvider

Holds one `SmithersGatewayClient` in context for every hook below. Pass a ready
`client`, or `options` and it constructs one. The client is memoized on
`baseUrl`/`token` so an inline `options` literal does not trigger a reconnect
storm.

```tsx
function SmithersGatewayProvider(props: {
  client?: SmithersGatewayClient;
  options?: SmithersGatewayClientOptions;
  mode?: WorkspaceMode;
  children?: ReactNode;
}): ReactElement;
```

<ParamField path="client" type="SmithersGatewayClient">
  A pre-built client. Takes precedence over `options`. See the
  [Gateway Client reference](/reference/gateway-client).
</ParamField>
<ParamField path="options" type="SmithersGatewayClientOptions">
  Client config (`baseUrl`, `token`, …) used to construct a client when none is
  passed.
</ParamField>
<ParamField path="mode" type="WorkspaceMode">
  Optional local or multiplayer collection mode. When omitted, local mode uses
  the client's base URL.
</ParamField>
<ParamField path="children" type="ReactNode" />

<Note>
The provider mounts both the raw `SmithersGatewayClient` context and the
TanStack DB collection provider. The live hooks and action hooks all work inside
this one provider.
</Note>

**Source** [`SmithersGatewayProvider.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/SmithersGatewayProvider.ts) · **Tests** [`SmithersGatewayProvider.test.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/tests/SmithersGatewayProvider.test.ts)

### createGatewayReactRoot

Mount a full custom UI in one call. Builds a client, mounts
`SmithersGatewayProvider`, creates the TanStack DB collection registry, and
renders your element. This is what a gateway-served `ui` entry uses.

```ts
function createGatewayReactRoot(
  element: ReactElement,
  options?: SmithersGatewayClientOptions & { rootId?: string; mode?: WorkspaceMode },
): SmithersGatewayClient;
```

<ParamField path="element" type="ReactElement" required>
  The app root to render.
</ParamField>
<ParamField path="options" type="SmithersGatewayClientOptions & { rootId?: string }">
  Client options plus `rootId` (the DOM element id to mount into; default
  `"root"`) and optional `mode`. Throws if the element is not found.
</ParamField>

<ResponseField name="SmithersGatewayClient" type="object">
  The client it created, for imperative use outside React.
</ResponseField>

```tsx
createGatewayReactRoot(<App />, { baseUrl: "/", rootId: "root" });
```

**Source** [`createGatewayReactRoot.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/createGatewayReactRoot.ts) · **See also** [Custom UI](/integrations/custom-ui)

### useSmithersGateway

Read the raw `SmithersGatewayClient` from context. Throws if called outside a
provider. The escape hatch for client methods the hooks do not wrap.

```ts
function useSmithersGateway(): SmithersGatewayClient;
```

**Source** [`useSmithersGateway.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useSmithersGateway.ts) · **See also** [Gateway Client](/reference/gateway-client)

### SmithersCollectionsProvider

Advanced provider for supplying a custom `WorkspaceMode`, `SmithersDataClient`,
or `QueryClient`. Most apps get this automatically from
[`SmithersGatewayProvider`](#smithersgatewayprovider).

```tsx
function SmithersCollectionsProvider(props: {
  mode?: WorkspaceMode;
  client?: SmithersDataClient;
  queryClient?: QueryClient;
  children?: ReactNode;
}): ReactElement;
```

<ParamField path="mode" type="WorkspaceMode">
  Local mode uses the Gateway REST API and SSE invalidation. Multiplayer mode
  uses Electric shapes for reads and the domain API for writes.
</ParamField>
<ParamField path="client" type="SmithersDataClient">
  Pre-built data client. Takes precedence over `mode`.
</ParamField>
<ParamField path="queryClient" type="QueryClient">
  TanStack Query client. A default client is created when omitted.
</ParamField>

**Source** [`SmithersCollectionsProvider.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/SmithersCollectionsProvider.ts)

### useSmithersCollections

Read the TanStack DB collection registry and data client from context. Use this
for custom `useLiveQuery` reads.

```ts
function useSmithersCollections(): {
  client: SmithersDataClient;
  collections: SmithersCollections;
  queryClient: QueryClient;
};
```

**Source** [`useSmithersCollections.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useSmithersCollections.ts) · **See also** [Sync](/guides/sync)

### useGatewayConnectionStatus

The link's connection lifecycle, derived from real transport traffic: RPC
resolves and stream frames mark it `online`, transport errors mark it `offline`,
auth failures mark it `unauthorized`.

```ts
function useGatewayConnectionStatus(): {
  status: "idle" | "connecting" | "online" | "offline" | "unauthorized";
  isOnline: boolean;
  reconnectingSince?: number;
};
```

<ResponseField name="status" type='"idle" | "connecting" | "online" | "offline" | "unauthorized"'>
  Current connection state.
</ResponseField>
<ResponseField name="isOnline" type="boolean">
  Shorthand for `status === "online"`.
</ResponseField>
<ResponseField name="reconnectingSince" type="number">
  Epoch ms of the first failure in the current offline streak, when offline.
</ResponseField>

**Source** [`useGatewayConnectionStatus.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/sync/useGatewayConnectionStatus.ts) · **Tests** [`sync.test.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/tests/sync/sync.test.ts)

## Data hooks

Each hook reads one gateway resource as a live TanStack DB collection. They
share the [`GatewayAsyncState`](#gatewayasyncstate) return shape unless noted,
and may be called unconditionally: pass `undefined` (or an empty `runId`) and
the hook resolves to an empty, stable state.

### useGatewayRun

Live single-run record. Seeds from `getRun`, then each lifecycle frame upserts
the row without a whole-tree refetch.

```ts
function useGatewayRun(
  runId: string | undefined,
): GatewayAsyncState<GatewayRunRow>;
```

<ParamField path="runId" type="string | undefined" required>
  The run to read. `undefined` yields an empty state.
</ParamField>
<ResponseField name="GatewayAsyncState<GatewayRunRow>" type="object">
  See [`GatewayAsyncState`](#gatewayasyncstate). `data` is the run record.
</ResponseField>

**Source** [`useGatewayRun.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayRun.ts) · **Tests** [`gateway-react.test.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/tests/gateway-react.test.ts)

### useGatewayRunEvents

Live, bounded run-event buffer (resilient stream with `afterSeq` resume).
Heartbeats are surfaced separately and never enter `events`; the array is capped
to `maxEvents`, most-recent wins. Returns its own shape, not `GatewayAsyncState`.

```ts
function useGatewayRunEvents(
  runId: string | undefined,
  options?: { afterSeq?: number; maxEvents?: number },
): {
  events: GatewayEventFrame[];
  lastHeartbeat: GatewayEventFrame | undefined;
  error: Error | undefined;
  streaming: boolean;
};
```

<ParamField path="runId" type="string | undefined" required />
<ParamField path="options.afterSeq" type="number">
  Drop events at or before this sequence number.
</ParamField>
<ParamField path="options.maxEvents" type="number" default="1000">
  Cap on the retained event buffer.
</ParamField>

<ResponseField name="events" type="GatewayEventFrame[]">
  Ordered, capped run events (heartbeats excluded).
</ResponseField>
<ResponseField name="lastHeartbeat" type="GatewayEventFrame | undefined">
  The most recent heartbeat frame, surfaced apart from `events`.
</ResponseField>
<ResponseField name="error" type="Error | undefined">
  Set when the stream fails (offline / unauthorized).
</ResponseField>
<ResponseField name="streaming" type="boolean">
  True while the stream is live.
</ResponseField>

**Source** [`useGatewayRunEvents.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayRunEvents.ts) · **See also** [Event types](/reference/event-types)

### useGatewayRuns

Live run list. Seeds from `listRuns`, re-pulls on invalidate.

```ts
function useGatewayRuns(
  params?: ListRunsRequest,
): GatewayAsyncState<GatewayRunSummaryRow[]>;
```

<ParamField path="params" type="ListRunsRequest">
  Optional filters (status, workflow, paging). Defaults to all runs.
</ParamField>
<ResponseField name="GatewayAsyncState<GatewayRunSummaryRow[]>" type="object">
  See [`GatewayAsyncState`](#gatewayasyncstate). `data` is the run summaries.
</ResponseField>

**Source** [`useGatewayRuns.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayRuns.ts) · **Tests** [`gateway-react.test.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/tests/gateway-react.test.ts)

### useGatewayWorkflows

Live registered-workflow list (`listWorkflows`).

```ts
function useGatewayWorkflows(
  params?: ListWorkflowsRequest,
): GatewayAsyncState<ListWorkflowsResponse>;
```

<ParamField path="params" type="ListWorkflowsRequest">
  Optional `filter`.
</ParamField>
<ResponseField name="GatewayAsyncState<ListWorkflowsResponse>" type="object">
  See [`GatewayAsyncState`](#gatewayasyncstate).
</ResponseField>

**Source** [`useGatewayWorkflows.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayWorkflows.ts)

### useGatewayApprovals

Live pending-approval list (`listApprovals`). Re-pulls when a run reaches
waiting-approval or after a `submitApproval`.

```ts
function useGatewayApprovals(
  params?: ListApprovalsRequest,
): GatewayAsyncState<ListApprovalsResponse>;
```

<ParamField path="params" type="ListApprovalsRequest">
  Optional filters, e.g. `{ filter: { runId } }` to scope to one run.
</ParamField>
<ResponseField name="GatewayAsyncState<ListApprovalsResponse>" type="object">
  See [`GatewayAsyncState`](#gatewayasyncstate). **`data` IS the array of pending
  gates** (`ListApprovalsResponse = GatewayApprovalSummary[]`); there is no
  `data.approvals` wrapper. Each gate is
  `{ runId, nodeId, iteration, requestTitle?, requestSummary?, workflowKey?, requestedAtMs }`
  (the title is `requestTitle`, not `title`). Pass `nodeId` + `iteration` straight
  into `submitApproval`.
</ResponseField>

```tsx
const approvals = useGatewayApprovals({ filter: { runId } });
const { submitApproval } = useGatewayActions();
return (approvals.data ?? []).map((gate) => (
  <button
    key={gate.nodeId}
    onClick={() => submitApproval({ runId, nodeId: gate.nodeId, iteration: gate.iteration, decision: { approved: true } })}
  >
    Approve {gate.requestTitle ?? gate.nodeId}
  </button>
));
```

**Source** [`useGatewayApprovals.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayApprovals.ts) · **See also** [`useGatewayActions`](#usegatewayactions)

### useGatewayCrons

Live cron-schedule list (`cronList`). Includes enabled **and** disabled rows;
re-pulls after a `cronCreate` / `cronDelete` / `cronRun`.

```ts
function useGatewayCrons(
  params?: CronListRequest,
): GatewayAsyncState<GatewayCronRow[]>;
```

<ParamField path="params" type="CronListRequest" />
<ResponseField name="GatewayAsyncState<GatewayCronRow[]>" type="object">
  See [`GatewayAsyncState`](#gatewayasyncstate).
</ResponseField>

**Source** [`useGatewayCrons.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayCrons.ts)

### useGatewayMemoryFacts

Live cross-run memory facts (`listMemoryFacts`). Pass a `namespace` to scope;
omit it for every namespace. Read-only on the wire, so query-only.

```ts
function useGatewayMemoryFacts(
  namespace?: string,
): GatewayAsyncState<GatewayMemoryFactRow[]>;
```

<ParamField path="namespace" type="string">
  Optional namespace filter.
</ParamField>
<ResponseField name="GatewayAsyncState<GatewayMemoryFactRow[]>" type="object">
  See [`GatewayAsyncState`](#gatewayasyncstate). `refetch` works; there is no
  write RPC.
</ResponseField>

**Source** [`useGatewayMemoryFacts.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayMemoryFacts.ts)

### useGatewayNodeOutput

On-demand output of one node (`getNodeOutput`). Built on
[`useGatewayRpc`](#usegatewayrpc); disabled until both ids are set.

```ts
function useGatewayNodeOutput(params: {
  runId: string | undefined;
  nodeId: string | undefined;
  iteration?: number;
}): GatewayAsyncState<GatewayRpcPayload<"getNodeOutput">>;
```

<ParamField path="params.runId" type="string | undefined" required />
<ParamField path="params.nodeId" type="string | undefined" required />
<ParamField path="params.iteration" type="number" default="0">
  Loop iteration to read. Pass `0` for a normal (non-looping) task.
</ParamField>
<ResponseField name="GatewayAsyncState<GetNodeOutput>" type="object">
  See [`GatewayAsyncState`](#gatewayasyncstate). `data` is a
  `{ status: "produced" | "pending" | "failed", row, schema, partial? }` envelope;
  **the task's output object is under `data.row`**, not `data` itself. Destructure
  defensively (some code paths hand you the row directly), the way `.smithers/ui/vcs.tsx`
  does:

  ```tsx
  const output = useGatewayNodeOutput({ runId, nodeId: "report", iteration: 0 });
  const isRecord = (v: unknown): v is Record<string, unknown> => typeof v === "object" && v !== null && !Array.isArray(v);
  const row = isRecord(output.data?.row) ? output.data.row : isRecord(output.data) ? output.data : {};
  // row now holds your Zod-typed task output, e.g. row.headline / row.summary
  ```
</ResponseField>

**Source** [`useGatewayNodeOutput.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayNodeOutput.ts)

### useGatewayScores

Live scorer/eval results for one run (`listScores`). Pass `nodeId` to scope to
one node. An empty `runId` resolves to a stable empty collection. Read-only, so
query-only.

```ts
function useGatewayScores(
  runId: string,
  nodeId?: string,
): GatewayAsyncState<GatewayScoreRow[]>;
```

<ParamField path="runId" type="string" required>
  The run to score. Empty string yields an empty state.
</ParamField>
<ParamField path="nodeId" type="string">
  Optional node scope.
</ParamField>
<ResponseField name="GatewayAsyncState<GatewayScoreRow[]>" type="object">
  See [`GatewayAsyncState`](#gatewayasyncstate).
</ResponseField>

**Source** [`useGatewayScores.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayScores.ts)

### useGatewayTickets

Live work docs (tickets, plans, specs, proposals) via `listTickets`. Tombstones
are filtered server-side, so every row is renderable. Pass a `kind` to scope.

```ts
function useGatewayTickets(
  params?: ListTicketsRequest,
): GatewayAsyncState<GatewayTicketRow[]>;
```

<ParamField path="params" type="ListTicketsRequest">
  Optional `kind` and other filters.
</ParamField>
<ResponseField name="GatewayAsyncState<GatewayTicketRow[]>" type="object">
  See [`GatewayAsyncState`](#gatewayasyncstate).
</ResponseField>

**Source** [`useGatewayTickets.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayTickets.ts)

### useGatewayPrompts

Live registered-prompt list (`listPrompts`, walked from `.smithers/prompts/`).
Read-only on the wire, so query-only; takes no arguments.

```ts
function useGatewayPrompts(): GatewayAsyncState<GatewayPromptRow[]>;
```

<ResponseField name="GatewayAsyncState<GatewayPromptRow[]>" type="object">
  See [`GatewayAsyncState`](#gatewayasyncstate).
</ResponseField>

**Source** [`useGatewayPrompts.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayPrompts.ts)

### useGatewayRpc

The generic escape hatch: call any gateway RPC by name and get its typed payload
back in `GatewayAsyncState`. The typed hooks above wrap this; reach for it when
no dedicated hook exists. Disable with `enabled: false`; a disabled or
key-changed query clears stale data instead of surfacing it.

```ts
function useGatewayRpc<Method extends GatewayRpcMethod>(
  method: Method,
  params: GatewayRpcParams<Method>,
  options?: { enabled?: boolean; deps?: readonly unknown[] },
): GatewayAsyncState<GatewayRpcPayload<Method>>;
```

<ParamField path="method" type="GatewayRpcMethod" required>
  The RPC method name.
</ParamField>
<ParamField path="params" type="GatewayRpcParams<Method>" required>
  Typed params for that method.
</ParamField>
<ParamField path="options.enabled" type="boolean" default="true">
  Skip the request when false.
</ParamField>
<ParamField path="options.deps" type="readonly unknown[]">
  Re-fetch trigger. Defaults to the serialized `params`.
</ParamField>
<ResponseField name="GatewayAsyncState<GatewayRpcPayload<Method>>" type="object">
  See [`GatewayAsyncState`](#gatewayasyncstate).
</ResponseField>

**Source** [`useGatewayRpc.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayRpc.ts) · **See also** [Gateway Client](/reference/gateway-client)

### useDelegationChain

Folded delegation-chain state for one run: the read model behind the
[`delegation-chain`](/workflows/delegation-chain) workflow UI. Enumerates every
physical `dc:*` node from the live run tree, fetches each node's durable output
row once (re-checked when that node finishes or fails), folds the rows through
the pure `foldDelegation` reducer, and returns the graph plus the delegation
actions. Malformed or unknown rows surface in `errors` instead of throwing.

```ts
function useDelegationChain(params: { runId: string | undefined }): {
  graph: DelegationGraph;
  loading: boolean;
  errors: unknown[];
  actions: {
    submitEdit(logicalId: string, editedOutput: unknown, note?: string): Promise<void>;
    skipPreviews(): Promise<void>;
    answerHuman(nodeId: string, iteration: number, value: unknown): Promise<void>;
    submitPoll(answers: DcPollAnswer[], comment?: string): Promise<void>;
  };
};
```

<ParamField path="params.runId" type="string | undefined" required>
  The run to fold. `undefined` (or an empty string) yields an empty graph.
</ParamField>

<ResponseField name="graph" type="DelegationGraph">
  Nodes keyed by logical id (status, version history, gates, attention
  rollups), edges, the current phase, pending question forms, the refined
  prompt, the predicted-vs-actual budget rollup, and run scores.
</ResponseField>
<ResponseField name="loading" type="boolean">
  True until the run tree and the first output sweep have hydrated.
</ResponseField>
<ResponseField name="errors" type="unknown[]">
  Fold issues (ignored rows) plus any tree/event/output fetch errors.
</ResponseField>
<ResponseField name="actions" type="object">
  <Expandable title="methods">
    <ResponseField name="submitEdit" type="(logicalId, editedOutput, note?) => Promise<void>">
      Deliver a live output edit on the durable `dc-edit` signal. The run
      treats it exactly like a plan-changing probe finding.
    </ResponseField>
    <ResponseField name="skipPreviews" type="() => Promise<void>">
      Skip the zero-backpressure preview phase (`dc-skip-preview` signal).
    </ResponseField>
    <ResponseField name="answerHuman" type="(nodeId, iteration, value) => Promise<void>">
      Answer a durable human request; `value` rides as JSON in the approval
      decision note.
    </ResponseField>
    <ResponseField name="submitPoll" type="(answers, comment?) => Promise<void>">
      Answer the pending end-of-run poll HumanTask.
    </ResponseField>
  </Expandable>
</ResponseField>

The pure reducer is exported alongside it (`foldDelegation`,
`parseDelegationNodeId`, `delegationTableForNodeId`) together with the
`DelegationGraph` and `Dc*Row` types, for tests and non-React consumers.

**Source** [`useDelegationChain.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/delegation/useDelegationChain.ts) · **See also** [`<DelegationChain>`](/components/delegation-chain)

## Action hooks

### useGatewayActions

Bound, memoized mutation methods off the client. Call them to drive a run:
launch, resume, cancel, hijack, rewind, approve/deny, signal, and manage crons.
Each takes the matching RPC params and returns a promise.

```ts
function useGatewayActions(): {
  launchRun: SmithersGatewayClient["launchRun"];
  resumeRun: SmithersGatewayClient["resumeRun"];
  cancelRun: SmithersGatewayClient["cancelRun"];
  hijackRun: SmithersGatewayClient["hijackRun"];
  rewindRun: SmithersGatewayClient["rewindRun"];
  submitApproval: SmithersGatewayClient["submitApproval"];
  submitSignal: SmithersGatewayClient["submitSignal"];
  cronCreate: SmithersGatewayClient["cronCreate"];
  cronDelete: SmithersGatewayClient["cronDelete"];
  cronRun: SmithersGatewayClient["cronRun"];
};
```

<ResponseField name="actions" type="object">
  <Expandable title="methods">
    <ResponseField name="launchRun" type="(params) => Promise<...>">
      Start a new run of a registered workflow.
    </ResponseField>
    <ResponseField name="resumeRun" type="(params) => Promise<...>">
      Resume a paused/stopped run.
    </ResponseField>
    <ResponseField name="cancelRun" type="(params) => Promise<...>">
      Halt agents and terminate a run.
    </ResponseField>
    <ResponseField name="hijackRun" type="(params) => Promise<...>">
      Hand off a resumable agent session.
    </ResponseField>
    <ResponseField name="rewindRun" type="(params) => Promise<...>">
      Fork from a checkpoint (time travel).
    </ResponseField>
    <ResponseField name="submitApproval" type="(params) => Promise<...>">
      Approve or deny a paused approval gate.
    </ResponseField>
    <ResponseField name="submitSignal" type="(params) => Promise<...>">
      Send a signal to a waiting node.
    </ResponseField>
    <ResponseField name="cronCreate / cronDelete / cronRun" type="(params) => Promise<...>">
      Manage and trigger background schedules.
    </ResponseField>
  </Expandable>
</ResponseField>

```tsx
const { launchRun, submitApproval } = useGatewayActions();
await launchRun({ workflow: "research", input: { topic: "smithers" } });
// `decision` is REQUIRED; that object carries the verdict. Pass `iteration`
// from the approval gate (default 0). Approve:
await submitApproval({ runId: RUN_ID, nodeId: NODE_ID, iteration: 0, decision: { approved: true } });
// Deny:
await submitApproval({ runId: RUN_ID, nodeId: NODE_ID, iteration: 0, decision: { approved: false, note: "not yet" } });
```

**Source** [`useGatewayActions.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayActions.ts) · **See also** [Gateway Client](/reference/gateway-client)

### useGatewayMutation

Typed mutation helper for supported domain writes. It calls the same domain API
as `useGatewayActions`, then invalidates the collection registry.

```ts
function useGatewayMutation<TVariables = Record<string, unknown>, TData = unknown>(
  method: string,
  options?: { invalidate?: readonly unknown[] },
): {
  mutate: (variables: TVariables) => Promise<TData>;
  mutateSafe: (variables: TVariables) => Promise<TData | undefined>;
  isLoading: boolean;
  error: Error | undefined;
};
```

<ParamField path="method" type="string" required>
  One of the supported domain mutations: `launchRun`, `resumeRun`, `cancelRun`,
  `hijackRun`, `rewindRun`, `submitApproval`, `submitSignal`, `cronCreate`,
  `cronDelete`, `cronRun`, `createTicket`, `updateTicket`, or `deleteTicket`.
</ParamField>
<ParamField path="options.invalidate" type="readonly unknown[]">
  Reserved for compatibility. Successful mutations invalidate the collection
  registry.
</ParamField>
<ResponseField name="result" type="object">
  `{ mutate, mutateSafe, isLoading, error }`.
</ResponseField>

**Source** [`useGatewayMutation.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/useGatewayMutation.ts) · **See also** [`useGatewayActions`](#usegatewayactions)

## Type exports

These types are exported from the same subpath for typing your own components.

<ResponseField name="GatewayAsyncState<T>" type="type">
  The shared data-hook return shape. See [above](#gatewayasyncstate).
</ResponseField>
<ResponseField name="SmithersCollectionsContextValue" type="type">
  `{ client, collections, queryClient }` from
  [`useSmithersCollections`](#usesmitherscollections).
</ResponseField>
<ResponseField name="GatewayConnectionState" type="type">
  Connection state used by [`useGatewayConnectionStatus`](#usegatewayconnectionstatus).
</ResponseField>

```ts
import type {
  SmithersCollectionsContextValue,
  GatewayConnectionStatus,
} from "smithers-orchestrator/gateway-react";
// GatewayAsyncState<T> is generic; see its section above for the full shape.
```

**Source** [`index.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/src/index.ts) · **Tests** [`gateway-react.test.ts`](https://github.com/smithersai/smithers/blob/main/packages/gateway-react/tests/gateway-react.test.ts)

---

This is the UI side of the control plane. For the underlying transport and its
imperative methods, see the [Gateway Client reference](/reference/gateway-client);
to embed a UI in a gateway-served workflow, see
[Custom UI](/integrations/custom-ui) and the
[Custom workflow UI guide](/guides/custom-workflow-ui).
