Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/eersnington/sideffect/llms.txt

Use this file to discover all available pages before exploring further.

Workflow is the entry point for defining a Cloudflare Workflow in Sideffect. It creates a typed workflow definition that you bind to an implementation and, from there, expose to the Cloudflare Workflows runtime. Import it directly from sideffect.
import { Workflow } from "sideffect";

Workflow.make(options)

Creates a typed WorkflowDefinition. The definition holds the workflow name and payload schema but does not contain any executable logic yet — call .toLayer(run) on the returned object to attach the implementation.
options.name
string
required
The Cloudflare Workflow name. Sideffect derives the entrypoint class name and the env binding name from this value automatically.
name valueClass nameenv binding
"image-processing"ImageProcessingIMAGE_PROCESSING
"add-numbers"AddNumbersADD_NUMBERS
"billing"BillingBILLING
options.payload
Schema.Schema<Payload>
required
An Effect Schema used to decode the raw Cloudflare workflow event payload before the run function executes. Decode failures are automatically converted to NonRetryableError so the workflow is not retried on invalid input.
Returns a WorkflowDefinition<Payload, Env> with a toLayer() method.

WorkflowDefinition.toLayer(run)

Binds a workflow definition to its implementation. The returned WorkflowLayer is what the Vite plugin discovers, or what you pass to WorkflowEntrypoints.make() when using plain Wrangler.
run
WorkflowRun<Payload, Result, Env>
required
The workflow implementation. Receives two arguments:
  • workflow: WorkflowContext<Payload, Env> — contains the decoded payload, the full Cloudflare event object (with decoded payload attached), env bindings, and the Worker ctx.
  • step: SideffectStep<Env> — the Sideffect step helper (see below).
run may be an async function, a synchronous function, or an Effect.Effect.
Returns a WorkflowLayer<Payload, Result, Env> consumed by the Vite plugin or WorkflowEntrypoints.make().

SideffectStep methods

The step object passed to every run function exposes four methods that mirror Cloudflare’s native WorkflowStep API, with added type safety and schema validation.

step.do(stepDefinition, payload, options?)

Runs a typed StepDefinition through Cloudflare’s native step system. Sideffect decodes the payload with the step’s payload schema before calling run, and decodes the result with the result schema before returning it to the workflow. Schema decode failures become NonRetryableError.
stepDefinition
StepDefinition<Payload, Result, Env>
required
A step created with Step.make(...), optionally decorated with Rollback.with(...)).
payload
Payload
required
The input value for the step. Must satisfy the step’s payload schema.
options
StepOptions
Optional Cloudflare step configuration. See StepOptions for the full shape.
Returns Promise<Result> — the decoded step result.

step.sleep(name, duration)

Pauses workflow execution for a fixed duration. The name is shown in the Cloudflare Workflows dashboard timeline.
name
string
required
A human-readable label for this pause, e.g. "wait before retry".
duration
string | number
required
How long to pause. Pass a Cloudflare duration string such as "1 second", "5 minutes", "2 hours", or a number of milliseconds.
Returns Promise<void>.

step.sleepUntil(name, timestamp)

Pauses workflow execution until a specific point in time.
name
string
required
A human-readable label for this pause.
timestamp
Date | number
required
A Date object or a Unix timestamp in milliseconds.
Returns Promise<void>.

step.waitForEvent(name, options)

Suspends the workflow until an external event of the given type arrives.
name
string
required
A human-readable label for this wait, e.g. "await approval".
options.type
string
required
The event type to wait for. Cloudflare matches events by this string.
options.timeout
string | number
Optional maximum wait duration. A Cloudflare duration string such as "24 hours" or a number of milliseconds. If the timeout elapses before the event arrives, Cloudflare terminates the wait.
Returns Promise<WorkflowStepEvent<A>>.

Complete example

The following workflow definition is taken from the Sideffect workbench. It demonstrates Workflow.make, toLayer, step.do, and step.sleep together.
import { Schema, Step, Workflow } from "sideffect";

// Step definitions
const addNumbers = Step.make("add numbers", {
  payload: Schema.Struct({ left: Schema.Number, right: Schema.Number }),
  result: Schema.Number,
  run: ({ left, right }) => left + right,
});

const multiplyNumber = Step.make("multiply number", {
  payload: Schema.Struct({ value: Schema.Number, by: Schema.Number }),
  result: Schema.Number,
  run: async ({ value, by }) => value * by,
});

const formatNumber = Step.make("format number", {
  payload: Schema.Number,
  result: Schema.String,
  run: (value) => `value:${value}`,
});

// Workflow definition
const addNumbersWorkflow = Workflow.make({
  name: "add-numbers",
  payload: Schema.Struct({ left: Schema.Number, right: Schema.Number }),
});

// Workflow layer (definition + implementation)
export const addNumbersLayer = addNumbersWorkflow.toLayer(async ({ payload }, step) => {
  const sum = await step.do(addNumbers, payload);
  const doubled = await step.do(multiplyNumber, { value: sum, by: 2 });
  const formatted = await step.do(formatNumber, doubled);

  return { sum, doubled, formatted };
});
Here is a more complete example from the README showing step.sleep and step.waitForEvent:
import { Schema, Step, Workflow } from "sideffect";

const workflow = Workflow.make({
  name: "image-processing",
  payload: Schema.Struct({ imageKey: Schema.String }),
});

const fetchImageStep = Step.make("fetch image", {
  payload: Schema.Struct({ imageKey: Schema.String }),
  result: Schema.Struct({ data: Schema.Uint8Array }),
  run: async (payload, ctx) => {
    const object = await ctx.env.BUCKET.get(payload.imageKey);
    const data = new Uint8Array(await object.arrayBuffer());
    return { data };
  },
});

const publishImageStep = Step.make("publish", {
  payload: Schema.Struct({ imageKey: Schema.String, imageData: Schema.Uint8Array }),
  result: Schema.Void,
  run: async ({ imageKey, imageData }, ctx) => {
    await ctx.env.BUCKET.put(`public/${imageKey}`, imageData);
  },
});

export const myWorkflowLayer = workflow.toLayer(async (event, step) => {
  const image = await step.do(fetchImageStep, { imageKey: event.payload.imageKey });

  await step.sleep("wait briefly", "1 second");

  await step.waitForEvent("await approval", { type: "approved", timeout: "24 hours" });

  await step.do(publishImageStep, {
    imageKey: event.payload.imageKey,
    imageData: image.data,
  });
});
The workflow name is the single source of truth for Cloudflare’s internal workflow name, the generated entrypoint class name, and the env binding. Keep it stable across deployments — renaming a live workflow requires a migration.

Build docs developers (and LLMs) love