Skip to main content
createDurableWorkflows is the main factory function that wires your workflow definitions into a production-ready execution engine running on Cloudflare Durable Objects. It returns two exports: Workflows (the Durable Object class) and WorkflowClient (a type-safe client factory).

Signature

function createDurableWorkflows<const W extends WorkflowRegistry>(
  workflows: W,
  options?: CreateDurableWorkflowsOptions
): CreateDurableWorkflowsResult<W>

Parameters

workflows
Record<string, WorkflowDefinition>
required
A record mapping workflow names to WorkflowDefinition objects created with Workflow.make. The object keys become the workflow names used when dispatching via the client. Use as const to preserve literal key types for full type safety.
const workflows = {
  processOrder: processOrderWorkflow,
  sendNotification: notificationWorkflow,
} as const;
options
CreateDurableWorkflowsOptions
Optional configuration for the engine.

Return value

Workflows
class
The Durable Object class. Export this from your worker entry point and register it in your wrangler.jsonc configuration. This class extends Cloudflare’s DurableObject and handles all lifecycle events (RPC calls, alarms, recovery).
WorkflowClient
WorkflowClientFactory
A factory object for creating type-safe client instances.

WorkflowClientInstance methods

All methods on WorkflowClientInstance return Effects, making them yieldable inside Effect.gen.

run

Start a workflow and wait synchronously for it to complete, pause, or fail.
run(request: WorkflowRunRequest): Effect.Effect<WorkflowRunResult, WorkflowClientError>
WorkflowRunResult
object

runAsync

Start a workflow and return immediately. The workflow runs in the background.
runAsync(request: WorkflowRunRequest): Effect.Effect<WorkflowRunResult, WorkflowClientError>

status

Get the current status of a workflow by instance ID.
status(instanceId: string): Effect.Effect<WorkflowStatus | undefined, WorkflowClientError>
WorkflowStatus is one of: "pending", "running", "paused", "completed", "failed", "cancelled".

completedSteps

Get the list of completed step names for a workflow instance.
completedSteps(instanceId: string): Effect.Effect<ReadonlyArray<string>, WorkflowClientError>

cancel

Cancel a running workflow. The workflow will fail with StepCancelledError on its next step execution.
cancel(
  instanceId: string,
  options?: { reason?: string }
): Effect.Effect<CancelResult, WorkflowClientError>
CancelResult
object

WorkflowRunRequest

The type-safe request object passed to run and runAsync. The workflow field must be a key from your registry, and input is automatically typed to match that workflow’s input type.
type WorkflowRunRequest = {
  workflow: keyof W;          // one of your registered workflow names
  input: WorkflowInput<W[K]>; // type-safe input for that workflow
  execution?: ExecutionOptions;
}
WorkflowRunRequest
object

Setup example

Step 1: Define and export workflows

// workflows.ts
import { Effect } from "effect";
import { Workflow, Backoff, createDurableWorkflows } from "@durable-effect/workflow";

const processOrderWorkflow = Workflow.make((orderId: string) =>
  Effect.gen(function* () {
    const order = yield* Workflow.step({
      name: "Fetch order",
      execute: fetchOrder(orderId),
    });

    yield* Workflow.sleep("3 seconds");

    yield* Workflow.step({
      name: "Process payment",
      execute: processPayment(order),
      retry: {
        maxAttempts: 5,
        delay: Backoff.exponential({ base: "1 second", max: "60 seconds" }),
      },
    });

    yield* Workflow.step({
      name: "Send confirmation",
      execute: sendEmail(order.email),
    });
  })
);

const workflows = {
  processOrder: processOrderWorkflow,
} as const;

export const { Workflows, WorkflowClient } = createDurableWorkflows(workflows, {
  tracker: {
    endpoint: "https://events.example.com/ingest",
    env: "production",
    serviceKey: "my-worker",
  },
  recovery: {
    maxRecoveryAttempts: 5,
    staleThresholdMs: 60_000,
  },
  purge: {
    delay: "5 minutes",
  },
});

Step 2: Export from worker entry point

// index.ts
import { Workflows } from "./workflows";

export { Workflows };

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // your fetch handler
  },
};

Step 3: Configure wrangler.jsonc

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2025-11-28",

  "durable_objects": {
    "bindings": [
      {
        "name": "WORKFLOWS",
        "class_name": "Workflows"
      }
    ]
  },

  "migrations": [
    {
      "tag": "v1",
      "new_classes": ["Workflows"]
    }
  ]
}

Using the client

Inside Effect.gen

import { Effect } from "effect";
import { WorkflowClient } from "./workflows";

Effect.gen(function* () {
  const client = WorkflowClient.fromBinding(env.WORKFLOWS);

  // Fire and forget
  const { id } = yield* client.runAsync({
    workflow: "processOrder",
    input: orderId,
    execution: { id: orderId }, // idempotency key
  });

  // Wait for completion
  const { id } = yield* client.run({
    workflow: "processOrder",
    input: orderId,
  });

  // Check status
  const status = yield* client.status(id);

  // List completed steps
  const steps = yield* client.completedSteps(id);

  // Cancel
  yield* client.cancel(id, { reason: "User requested cancellation" });
});

With 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