Workflows

A workflow is a plain async function marked 'use workflow' that orchestrates 'use step' units. Each step runs once, persists its result, and retries on failure. The whole run survives crashes and redeploys, so it can pause for minutes, days, or months and resume where it stopped.

An agent holds an open-ended conversation. A workflow runs a fixed, auditable pipeline: validate, call a model, write a side effect. Stackbone takes the durability from the upstream Workflow SDK (the same engine behind Vercel Workflows) and runs it on a per-install Redis-backed runtime.

The execution model

  • Two directives do the work. 'use workflow' on the function makes it durable and replayable. 'use step' on a sub-function makes that unit run once, persist its result, and retry on failure. Every side effect belongs in a step.
  • The body must be deterministic, because the runtime replays it on every crash-resume. Keep it free of clocks, randomness, and direct I/O. Steps hold the real work, and the runtime replays their recorded results instead of re-running them.
  • Only a step may touch the platform client. The body runs in an isolated sandbox with no Node.js access, so reference stackbone.* only inside a 'use step' function. One stackbone.* reference in the body, or in a helper the body calls, fails every workflow in the workspace at run time.
  • Sibling inputSchema and outputSchema Zod exports are the workflow's typed contract. The runtime validates the input before a run starts.
  • Discovery is by convention. The runtime picks up any workflows/<name>.workflow.ts, and the exported function is the camelCase name plus Workflow.
  • One step has a time ceiling. It runs inside a single delivery to the runtime, capped at 15 minutes by default. The runtime abandons a step that runs past the ceiling and does not retry it. The abandoned copy may still be running, and a retry would start a second one beside it. Raise WORKFLOW_HTTP_TIMEOUT_MS on the deployment when a step needs longer, such as one long model call over a large input.
workflows/qualify-lead.workflow.ts
import { z } from '@stackbone/sdk';

export const inputSchema = z.object({ email: z.email(), company: z.string() });
export const outputSchema = z.object({ score: z.number(), qualified: z.boolean() });

export async function qualifyLeadWorkflow(input: z.infer<typeof inputSchema>) {
  'use workflow';
  const score = await scoreLead(input.company); // a durable step
  return { score, qualified: score >= 60 };
}

async function scoreLead(company: string) {
  'use step';
  return company.length * 5;
}

Kill the runtime mid-run and it resumes from the last completed step instead of replaying everything.

Where to go next

  • Getting started: scaffold, trigger, and observe your first workflow, including long waits and approvals.
  • Examples: an onboarding pipeline, a refund with a human approval, and a scheduled digest.
  • Serial execution: make a workflow run one at a time, in arrival order, when two copies must never overlap.
  • Workflow agents: let a step delegate to an agent.
  • Human-in-the-loop: pause a run for a person to approve.
BUILT WITH ❤️ FROM CANADA AND SPAIN