Workflow Definitions
A workflow definition is plain, storable data — a trigger, a list of nodes, and a list of edges connecting them. This is the shape you pass to Workflow::fromArray(), and the shape returned by Workflow::toArray() / toJson().
[
'id' => 'send-welcome-email',
'name' => 'Send Welcome Email',
'version' => 1,
'trigger' => [
'type' => '@wf::trigger.manual',
'config' => [],
],
'nodes' => [
[
'id' => 'log',
'type' => '@wf::action.log',
'config' => ['level' => 'info', 'message' => 'Hello {{trigger.email}}'],
],
],
'edges' => [
['from' => 'trigger', 'to' => 'log', 'branch' => 'main'],
],
'meta' => [],
'active' => true,
]
Trigger
'trigger' => ['type' => string, 'config' => array]
type is a registered trigger's type string (see Triggers); config is whatever that trigger's schema() declares. There is exactly one trigger per workflow, and it's always addressed as the synthetic node id 'trigger' in edges — it's never listed in nodes.
Nodes
Each entry in nodes is:
['id' => string, 'type' => string, 'config' => array]
idis unique within the workflow — you choose it (e.g.'log','check-age'). It's how edges and expressions ({{nodes.check-age.result}}) refer back to this step.typeis a registered node's type string — see Built-in Nodes for the full list, or Custom Nodes to register your own.configis whatever that node'sschema()declares (each node's reference page documents its fields).
Node type strings
A node's type() is just a unique string — the registry uses it as an opaque lookup key, so any punctuation is fine as long as it's unique. Built-in nodes are namespaced under @wf::, followed by a group and an action separated by a ., e.g. @wf::action.log, @wf::model.create. Your own custom nodes can use any type string you like (see Custom Nodes) — the examples throughout this documentation use an app:: namespace by convention, to keep them visually distinct from built-ins.
Edges
Each entry in edges is:
['from' => string, 'to' => string, 'branch' => string]
from/toare node ids (or'trigger'for the first edge).branchselects which of the source node's outcomes this edge follows. Most nodes only ever produce one outcome, sobranchis'main'. Nodes that can branch use other names — e.g. a Condition node produces'true'or'false', and a Switch node produces whichever case branch matched (or'default').
Always include branch explicitly — it's matched exactly, so an edge without it won't be found when the engine looks up 'main'.
A plain node has at most one outgoing edge. Only branch-producing nodes (Condition, Switch, Loop, Scope) can have more than one outgoing edge, one per branch.
Versioning
version is a plain integer you control. The repository contract doesn't bump it automatically on update() — increment it yourself if each save should represent a new version. The interactive builder does this for you.
meta and active
meta is a free-form array for your own application's use (the engine doesn't read it). active is a convention flag — see Workflows § Active vs. inactive.
Next
- Execution — running a definition and inspecting the result.
- Expressions — the
{{ }}syntax referenced inconfigvalues above.