DromioWorkflow SDK
Workflow SDKAuthoringAPIWorkflow Apps & Harnesses

Start here

OverviewQuick start
Start here
Overview
Quick start

Quick start

Build and run a contracted workflow in one file. These examples use Bun 1.3, the runtime used by the SDK's own CI and executable Workflow App runners.

Install​

Create an empty TypeScript project and install the beta package:

mkdir hello-workflow
cd hello-workflow
bun init -y
bun add @dromio/workflow@^0.2.5 zod@^4.1.12

@dromio/workflow is an ESM package. Zod 4 is installed directly because the example imports it for runtime contracts. Pin the beta SDK version so upgrades are deliberate.

Define a step and workflow​

Create hello.ts:

import { step, workflow } from "@dromio/workflow";
import { z } from "zod";

const greet = step({
id: "hello.greet",
input: {name: z.string().min(1)},
output: {greeting: z.string().min(1)},
run: ({input}) => ({greeting: `Hello, ${input.name}!`}),
});

export const helloWorkflow = workflow({
catalog: [greet],
document: {
id: "hello-workflow",
version: 1,
trigger: {
id: "request",
type: "manual",
input: {name: {jsonSchema: {type: "string", minLength: 1}}},
},
nodes: [{id: "greet", catalogItemId: greet.id}],
edges: [
{id: "request-to-greet", source: "request", target: "greet"},
{id: "greet-to-result", source: "greet", target: "result"},
],
end: {
id: "result",
type: "result",
output: {greeting: {jsonSchema: {type: "string", minLength: 1}}},
},
},
input: {name: z.string().min(1)},
output: {greeting: z.string().min(1)},
});

if (import.meta.main) {
const session = await helloWorkflow.start({name: "Ada"});

if (session.status !== "completed") {
throw new Error(`Unexpected run status: ${session.status}`);
}

console.log(session.state.greeting);
}

The workflow document is intentionally explicit: the trigger and result are boundaries, the node points to a catalog item, and edges define execution order. Step logic stays in TypeScript. The JSON Schemas make the serialized document portable; the matching Zod sources preserve exact TypeScript inference and runtime parsing. When both are present, their keys and accepted values must agree. Workflows covers this boundary in detail.

Run it​

bun run hello.ts

Expected output:

Hello, Ada!

workflow.start() validates the trigger input, executes the graph, validates step outputs, and returns a session. The session contains status, state, events, checkpoints, runId, and methods for waiting, resuming, cancelling, and rerunning.

Watch lifecycle events​

Pass an event sink when starting a workflow:

const session = await helloWorkflow.start({name: "Ada"}, {
onEvent(event) {
console.log(event.index, event.type, event.stepId ?? "run");
},
});

Events are also retained in session.events. Use the callback to stream them to logs or telemetry and the retained array for inspection after the run.

Add an executable surface​

Keep the direct start() example as hello.ts. Create a separate cli.ts when you want a packaged CLI, TUI, GUI, SVG, or server adapter:

import { runWorkflowCli, workflowApp } from "@dromio/workflow";
import { helloWorkflow } from "./hello.js";

const app = workflowApp({
id: "hello-app",
workflows: [helloWorkflow],
});

await runWorkflowCli(app);

The prompt-based app runners require exactly one trigger input key, which this workflow has. Run it with:

bun run cli.ts Ada

import.meta.main is false when cli.ts imports the workflow, so the direct example does not run again. After the script path and any runner flags, the remaining positional arguments (Ada here) are joined into one prompt and mapped to the workflow's single trigger input key. This starts only the CLI-driven run.

Next, read Steps to choose primitives, Workflows for document rules, and Execution and durability before adding waits or external delivery.

On this page

  • Install
  • Define a workflow
  • Run it
  • Watch events
  • Add a surface