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-leadThis 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:
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-leadOver 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
- Examples: an onboarding pipeline, a refund with a human approval, and a scheduled digest.
- What is a workflow: the directives, durability, and typed-contract model in full.
- Workflow + Agents: delegate a step to an agent.
- Human-in-the-loop: pause a run for a person to approve.