Workflow YAML reference
The slim format dibbla wf create -f consumes — three node types, the edge syntax, and the handful of rules that decide whether a graph runs.
The format dibbla wf create -f and wf update -f read. Write this, not the
verbose React-Flow JSON the editor stores — the CLI converts between them.
Build your first workflow is the walkthrough; this is the field reference.
The whole shape
name: incident_triage # required, unique in the org
nodes:
- id: api_input # entry point
type: api
inputs: [question] # what a caller must send
outputs: [question] # what downstream nodes can read
- id: reasoning_agent_function
type: function
function: reasoning_agent_function # from the registry
server: function-server # which execution server provides it
inputs: # literals, or left for an edge to fill
system_message: "You are an incident triage assistant. Be brief."
model: dibbla/claude-haiku-4-5
outputs: [response]
- id: api_response
type: api_response
linked_to: api_input # ties the reply to its entry node
inputs: [response]
edges:
- api_input.question -> reasoning_agent_function.prompt_message
- reasoning_agent_function.response -> api_response.response
Node types
type | Purpose | Fields you need | Enforced by wf validate |
|---|---|---|---|
api | entry point; defines what callers send | id, inputs, outputs | nothing beyond type — outputs defaults to inputs |
function | one call to a registry function | id, function, server | function, server |
api_response | what the caller receives | id, linked_to, inputs | linked_to |
The two columns differ on purpose. The validator is lenient — an api node
with no ports passes — but a graph built that way cannot do anything. Write
the middle column; do not read a clean validate as confirmation you did.
An api node is required for every execution, including from the CLI.
dibbla wf execute posts to the same endpoint the HTTP caller does, and it
returns No API node found without one. The api_response node is what makes
a caller receive something: omit it and a blocking execute waits out the
30-minute timeout and returns 408.
Edges
One per line, from.output -> to.input. The left side must be an output the
producing node declares; the right side an input the consuming node accepts.
edges:
- api_input.question -> summarise_incidents.region
- summarise_incidents.summary -> api_response.response
The rules that actually bite
Adding a plausible output a function does not have — an error port, say —
produces preflight: malformed source handle, and the run then hangs
rather than failing. A hang and a typo look nothing alike, which is what
makes this one expensive. dibbla fn get <server> <function> is the ground
truth for what exists.
Custom ids work: edges and tools: resolve against them when you create the
workflow. What they do not do is come back. The platform stores its own
internal ids, and wf get regenerates a slim id from each node’s label —
which for a function node defaults to the function name. So the file you
wrote and the file you read back differ, and a diff between them is noise
rather than drift.
A node may be an agent’s tool or a data input to that agent — never
both. Wiring both closes a cycle (the implicit tool edge plus your data edge)
and the run is rejected with CYCLE_DETECTED before it starts. To put data
in an agent’s context, template it into the system message and keep that node
out of tools:.
The validator builds its cycle check from edges: alone and never adds the
implicit edges from tools:, so a tool-induced cycle passes both validate
and create cleanly and only fails when you trigger it. It is the one class
of error the section below cannot protect you from.
Types must match the function’s reflected types. Send true, not
"true"; 42, not "42". dibbla fn get prints them.
Validate before you create
$dibbla wf validate -f incident_triage.yaml valid: true node_count: 3 edge_count: 2 $dibbla wf create -f incident_triage.yaml # snapshot HEAD before editing an existing one $dibbla revisions create incident_triage $dibbla wf update incident_triage -f incident_triage.yaml
validate changes nothing on the platform, so it is cheap to run on every
edit — but it is a server call, not a local parse. It needs your token, and it
resolves the live function registry, so a function whose execution server
is currently offline validates differently from one that is connected.
dibbla wf get <name> -o yaml prints any workflow in this format — usually
faster than reading a schema. Note it returns the converted form, so a
node you named yourself comes back named after its function — see the
round-trip warning above.
Updating safely
wf update sends the current ETag, so a concurrent edit returns 412 rather
than silently overwriting someone. Pull, merge, retry — --force exists but
discards whatever the other writer just shipped.
And remember the editor has no save button: every change made there writes the
live HEAD immediately. Take a revision first.