Getting started with workflows

This page takes you from an empty workspace to a workflow you can trigger and watch run. Workflows run locally today through stackbone dev, so everything here happens on your machine. If the CLI is new to you, set up Node 24 and Docker with the CLI getting started guide first.

A workflow lives inside a workspace alongside your agents. The steps below assume you already have one (stackbone init my-workspace).

1. Scaffold a workflow

From inside your workspace:

stackbone add workflow qualify-lead

This writes workflows/qualify-lead.workflow.ts. It needs no login and touches no cloud, because workflows run locally today. It only writes new files, and a name collision fails with a clear error (re-run with --force to overwrite).

The file follows the naming convention the runtime scans for: the workflow name is the file basename without .workflow.ts, and the exported function is the camelCase name plus Workflow. So qualify-lead.workflow.ts exports qualifyLeadWorkflow.

2. Write the pipeline

A workflow is a plain async function marked 'use workflow'. Every side effect goes in a sub-function marked 'use step', which the runtime runs once, persists, and retries on failure. Declare the contract with sibling inputSchema and outputSchema exports:

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({
  email: z.string(),
  score: z.number(),
  qualified: z.boolean(),
});

export async function qualifyLeadWorkflow(input: z.infer<typeof inputSchema>) {
  'use workflow';

  const enriched = await enrich(input.company); // step 1: fetch firmographics
  const score = await scoreLead(enriched); // step 2: deterministic scoring

  return { email: input.email, score, qualified: score >= 60 };
}

async function enrich(company: string) {
  'use step';
  // Replace with a real lookup. A step's result is persisted and replayed on resume.
  return { company, employees: 250, industry: 'software' };
}

async function scoreLead(enriched: { employees: number }) {
  'use step';
  return Math.min(100, Math.round(enriched.employees / 5));
}

Keep the workflow body deterministic: no clocks, no randomness, no direct I/O. Put all of that inside a step. See the overview for why.

3. Triggering a run

A workflow starts as its own run, with its own run id and step log. The two paths you will use most while developing:

From the CLI. Start a run by name with a JSON input:

stackbone workflows start qualify-lead \
  --input '{"email":"sam@acme.com","company":"Acme"}'

List what the installation exposes, or inspect a workflow's contract, with:

stackbone workflows list
stackbone workflows schema qualify-lead

Over HTTP. The emulator serves a generic trigger route per workflow. The body is the workflow input, validated against inputSchema before anything runs:

curl -X POST http://127.0.0.1:4242/api/workflows/qualify-lead/start \
  -H 'content-type: application/json' \
  -d '{"email":"sam@acme.com","company":"Acme"}'
# → { "workflowName": "qualify-lead", "runId": "...", "worldRunId": "...", "trigger": "http" }

A bad payload is rejected with field-level issues and no run is created.

4. Watch the run

A started workflow is an ordinary run. Observe it, retry a failed step, or read its logs with:

stackbone runs list
stackbone runs get <runId>
stackbone logs tail --run <runId>

Kill stackbone dev mid-run and start it again: the run resumes from the last completed step instead of replaying the whole pipeline. That is the durability the two directives buy you.

5. Run on a schedule

To run the workflow on a cadence, export a schedules array next to it. The runtime reconciles it on every boot, so there is no imperative call to make:

export const schedules = [{ cron: '0 3 * * *', input: { email: '', company: '' } }];

The cron value is a five-field pattern (minute hour day month weekday) or a named macro (@hourly, @daily, @weekly, @monthly, @yearly). A bad pattern fails when you register it, not silently at the first missed tick.

Where to go next

The platform for agent developers.Build, ship and observe agentsthat survive prod.

© 2026 · STACKBONEBUILT WITH ❤️ FROM CANADA AND SPAIN