> For the complete documentation index, see [llms.txt](/llms.txt).
> A full single-fetch corpus is available at [llms-full.txt](/llms-full.txt).
---
last_verified: 2026-10-09
title: Workflows
description: Define workflows with workflow(), checkpoint steps, keep state, and run child workflows
category: TypeScript SDK
hideRightSidebar: true
---

A workflow is a durable program made of checkpointed steps. When a worker restarts, the workflow replays and each completed step returns its saved result instead of running again. For a guided introduction, see [Workflows](/docs/build/workflows.md).

## `workflow(name, handler, options?)`

```typescript
import { fn, workflow } from '@agnt5/sdk';
import type { Context } from '@agnt5/sdk';

const double = fn('double').run(async (ctx: Context, input: { n: number }) => input.n * 2);

export const doubleTwice = workflow('double_twice', async (ctx: Context, input: { n: number }) => {
  const once = await ctx.step('first', () => double(ctx, { n: input.n }));
  const twice = await ctx.step('second', () => double(ctx, { n: once }));
  return { once, twice };
});
```

The handler receives the [`Context`](/sdk/typescript/context.md) and the run's input, and returns a JSON-serializable result. The workflow registers when its module is imported.

### `WorkflowOptions`

| Option | Description |
|---|---|
| `description` | What the workflow does. |
| `inputSchema`, `outputSchema` | JSON Schema for the input and output. |
| `cron` | A cron expression that starts the workflow on a schedule. |
| `triggers` | Event triggers, built with `event(name)` or `webhook(source, { event })`. See [Webhooks](/docs/build/webhooks.md). |
| `flowControl` | Flow-control policy for [serverless endpoints](/docs/run/serverless-endpoints.md). |

```typescript
import { workflow, event } from '@agnt5/sdk';

export const onOrder = workflow(
  'on_order',
  async (ctx: Context, input: { orderId: string }) => ({ received: input.orderId }),
  {
    description: 'Handle a new order',
    inputSchema: { type: 'object', properties: { orderId: { type: 'string' } } },
    triggers: [event('order.created')],
  },
);
```

## Steps

### `ctx.step(name, fn, options?)`

Runs `fn` and checkpoints its result under `name`. On replay, a completed step returns the saved result without running `fn` again.

```typescript
const total = await ctx.step('fetch_total', () => fetchTotal(ctx, { orderId }));
```

| Option | Description |
|---|---|
| `key` | A stable identity for the step. Required when the same step name runs in a loop, in parallel, or in a different order, so each one replays the right result. |
| `input` | Data passed to `fn` as its argument and recorded with the step. Replay checks it: a step whose input changes fails the run with `NON_DETERMINISTIC_REPLAY` instead of returning a result recorded for other values. |

```typescript
// One checkpoint per item, matched by key
const doubled = await Promise.all(
  [1, 2, 3].map((n) => ctx.step('double', () => double(ctx, { n }), { key: `n-${n}` })),
);

// The step body receives its input
const withTax = await ctx.step('add_tax', ({ amount }) => amount * 1.2, { input: { amount: 100 } });
```

Derive `input` from the workflow's input or earlier step results, pass plain data (dates as ISO strings, `null` rather than `undefined`), and keep secrets out of it: the input is shown on the step's record.

### Run steps concurrently

`parallel()` and `gather()` wait for several promises, typically steps, and return their results:

```typescript
import { parallel, gather } from '@agnt5/sdk';

const [a, b] = await parallel([
  ctx.step('a', () => double(ctx, { n: 1 })),
  ctx.step('b', () => double(ctx, { n: 2 })),
]);

const { sales, inventory } = await gather({
  sales: ctx.step('sales', () => fetchSales(ctx, { reportId })),
  inventory: ctx.step('inventory', () => fetchInventory(ctx, { reportId })),
});
```

### Undo completed steps: `saga()`

`saga(ctx, steps)` runs `[action, compensation]` pairs in order. If an action fails, the compensations of the actions that already completed run in reverse order, then the error is rethrown:

```typescript
import { saga } from '@agnt5/sdk';

await saga(ctx, [
  [() => reserveSeat(ctx, input), () => releaseSeat(ctx, input)],
  [() => chargeCard(ctx, input), () => refundCard(ctx, input)],
]);
```

## Durable sleep

`ctx.sleep(ms, name?)` pauses the workflow durably; the standalone `sleep(ctx, ms, name?)` does the same. Durations are in milliseconds.

```typescript
await ctx.sleep(24 * 60 * 60 * 1000, 'wait_24h');
```

## Workflow state

State is scoped to the run and survives replay. All three methods are async:

| Method | Description |
|---|---|
| `await ctx.set(key, value)` | Store a JSON-serializable value. |
| `await ctx.get<T>(key, defaultValue?)` | Read a value, or `defaultValue` when the key is not set. |
| `await ctx.delete(key)` | Remove a key. Returns `true` when it existed. |

## Child workflows

Run other workflows from a workflow. Each child is its own run, and the parent waits for its result. Pass the child's registered name or its handler:

| Function | Returns |
|---|---|
| `executeChildWorkflow(ctx, workflow, input)` | The child's result. |
| `fanOut(ctx, workflow, inputs[])` | One result per input, in order. |
| `parallelWorkflows(ctx, [[workflow, input], ...])` | One result per pair, in order. |
| `gatherWorkflows(ctx, { name: [workflow, input], ... })` | An object of results, by name. |
| `race(ctx, [[workflow, input], ...])` | The result of the first child to finish. |
| `withTimeout(ctx, workflow, input, timeoutMs)` | The child's result, or an error when it takes longer than `timeoutMs`. |

```typescript
import { executeChildWorkflow, fanOut } from '@agnt5/sdk';

export const report = workflow('report', async (ctx: Context, input: { ids: string[] }) => {
  const summary = await executeChildWorkflow(ctx, 'summarize', { ids: input.ids });
  const details = await fanOut(ctx, 'fetch_detail', input.ids.map((id) => ({ id })));
  return { summary, details };
});
```

## Next steps

* [Context API](/sdk/typescript/context.md): waiting for people and signals, logging, and progress.
* [Functions](/sdk/typescript/functions.md): define the work that steps run.
* [Workflows guide](/docs/build/workflows.md): durability, replay, and idempotent side effects in practice.
