Building workflows
Author workflow definitions dynamically — the draft builder and graph conversion.
Beyond referencing workflows published from the Studio editor, you can build
definitions dynamically in code and run them inline. Two tools help, both
producing a plain Workflow object.
The draft builder
createWorkflow() gives you an imperative builder with connection-type
checking and validation:
import { BlitClient, createWorkflow, getNode, run } from "@blitflow/sdk";
const client = new BlitClient({ apiKey: process.env.BLITFLOW_TOKEN });
const draft = createWorkflow(client);
// declare workflow inputs (returns a ref string like "${prompt.value}")
const promptRef = draft.addInput("prompt", "TEXT");
// add nodes by spec id, wiring inputs to refs or literals
const gen = draft.addNode("rd-fast", {
prompt: promptRef,
removeBg: true,
seed: 7,
});
// or connect ports explicitly — returns { ok, reason? } on type mismatch
// draft.connect(gen.id, "image", upscale.id, "init");
// expose outputs
draft.addOutput("sprite", `\${${gen.id}.image}`);
// validate before running
const problems = draft.validate();
if (problems.length > 0)
throw new Error(problems.map((p) => p.message).join("\n"));
const workflow = draft.toWorkflow();
const outputs = await run(client, workflow, {
prompt: { kind: "text", value: "a brass key" },
});| Method | Purpose |
|---|---|
addInput(name, connector) | Add an input node; returns its ${name.value} ref |
addNode(specId, inputs?) | Add a node from the palette |
connect(fromId, fromPort, toId, toPort) | Wire an edge with connector-type checking |
addOutput(name, ref) | Add an output node exposing a result |
validate() | Returns a list of validation errors (empty = OK) |
toWorkflow() | Produce the final Workflow JSON |
Fetch node specs first (getNode / searchNodes) so you wire real port names
and valid enum values — see Nodes & specs.
graphToWorkflow() — convert a visual graph
If your application maintains its own node-and-edge graph model (like the
Studio editor does), graphToWorkflow() converts it into a definition. It is
spec-aware: it resolves port types, turns exposed ports into input nodes,
and materializes primitives as input or const nodes.
import { graphToWorkflow } from "@blitflow/sdk";
const workflow = graphToWorkflow({
nodes: [
{
id: "n1",
specId: "rd-fast",
values: { seed: 7 },
exposed: ["prompt"],
},
],
edges: [
// { source: "n1", sourceHandle: "image", target: "n2", targetHandle: "init" }
],
specsById, // Record<string, NodeSpec> — from searchNodes()/getNode()
outputNodes: [{ id: "out1", label: "sprite" }],
primitives: [],
});| Argument | Description |
|---|---|
nodes | Graph nodes: specId, inline socket values, and exposed input ports (become workflow inputs) |
edges | Connections: source/sourceHandle → target/targetHandle |
specsById | Node specs used to resolve port types |
outputNodes | Output sinks — each label becomes a workflow output name |
primitives | Constants and workflow-input placeholders feeding the graph |
Publishing
Programmatically built workflows are best run inline — pass the object
straight to run() / POST /v1/runs. Publishing versioned workflows (and
rolling back @latest) happens in the Studio; publish there when you want a
stable <id>@<version> ref for other systems to consume.