Skip to main content
WorkflowClient provides a type-safe interface for invoking and managing workflows. The client is generated by createDurableWorkflows and knows the names and input types of all your registered workflows.

Creating a client

import { WorkflowClient } from "./workflows";

const client = WorkflowClient.fromBinding(env.WORKFLOWS);
fromBinding accepts the Durable Object namespace binding from your Env. It does not start any workflow — it returns a client object whose methods return Effects.

Starting workflows

client.run() — synchronous

Starts a workflow and waits for it to reach a terminal or paused state before returning. Use this when the caller needs to know the immediate outcome.
const { id } = yield* client.run({
  workflow: "processOrder",
  input: orderId,
});
run returns when the workflow completes, fails, or pauses (e.g. on a Workflow.sleep). The returned id is the workflow instance ID.

client.runAsync() — asynchronous

Starts a workflow and returns immediately without waiting for it to finish. Use this when you want to fire-and-forget or track the workflow separately.
const { id } = yield* client.runAsync({
  workflow: "processOrder",
  input: orderId,
});

Custom execution IDs

Both run and runAsync accept an optional execution object with a custom id. This lets you use a natural key (like an order ID) to make workflow starts idempotent:
const { id } = yield* client.runAsync({
  workflow: "processOrder",
  input: orderId,
  execution: { id: orderId },
});
If a workflow with that ID already exists, the call returns its current ID rather than starting a duplicate.

Querying state

client.status(workflowId)

Returns the current status of a workflow instance:
const status = yield* client.status(workflowId);
// status._tag: "Running" | "Paused" | "Completed" | "Failed" | "Cancelled"

client.completedSteps(workflowId)

Returns the names of all steps that have completed successfully:
const steps = yield* client.completedSteps(workflowId);
// steps: readonly string[]

Cancelling a workflow

yield* client.cancel(workflowId, { reason: "User requested cancellation" });
The reason field is optional. Once cancelled, the workflow transitions to a terminal Cancelled state and will not resume.

Using in Effect.gen

All client methods return Effects, so they compose naturally in Effect.gen:
Effect.gen(function* () {
  const client = WorkflowClient.fromBinding(env.WORKFLOWS);

  const { id } = yield* client.runAsync({
    workflow: "processOrder",
    input: orderId,
    execution: { id: orderId },
  });

  const status = yield* client.status(id);
  const steps = yield* client.completedSteps(id);
});

Using with Effect.runPromise

If you need the client outside of an Effect context — for example, in a fetch handler — wrap the call in Effect.runPromise:
const client = WorkflowClient.fromBinding(env.WORKFLOWS);

const { id } = await Effect.runPromise(
  client.runAsync({
    workflow: "processOrder",
    input: orderId,
    execution: { id: orderId },
  })
);

Build docs developers (and LLMs) love