Data Flow
Workflow data moves through ports and context variables. The graph decides where execution goes; set, in, and recipes decide what data each element sees.
Context variables
Section titled “Context variables”Store element output with set.
{ "name": "loadProject", "type": "source/entities", "options": { "module": "projects", "first": true }, "set": { "project": "default:" }}After this element runs, later recipes can read $project.
Common bindings:
| Binding | Meaning |
|---|---|
default: | Data emitted from the default output port. |
input: | Request or event input payload. |
confirm: | Data confirmed by an action/modal. |
error: | Error output where an element supports it. |
event: | Event stream payload from elements that emit progress or service events. |
:selector | Special event data such as the field selector in on_change flows. |
Input bindings
Section titled “Input bindings”Use in to pass a context value to a specific input port.
{ "name": "saveProject", "type": "action/save", "in": { "default": "$project" }}Without in, many elements receive the data that arrived at their connected input port. Use explicit in bindings when a workflow has multiple resources in context and the target element must operate on one of them.
Recipes
Section titled “Recipes”Recipes calculate values from the current context.
{ "name": "prepareSummary", "type": "op/recipe", "options": { "recipe": "{\"title\": $project.name, \"task_count\": count($tasks)}" }, "set": { "summary": "default:" }}Use recipes for compact transformations. If the expression becomes difficult to read, split it into several named op/recipe or op/set elements.
Read more in the Recipes reference.
Setting resource fields
Section titled “Setting resource fields”op/set changes the workflow data object. It is commonly used after source/new or after loading an existing resource.
{ "name": "setTaskFields", "type": "op/set", "options": { "copy": "$taskData", "recipes": { "project": "$project", "status": "\"open\"", "assigned_to": "user()" } }}Use copy when user input should become the base object. Use recipes for fields that must be calculated or controlled by the workflow.
Arrays and loops
Section titled “Arrays and loops”Use flow/each when every resource in an array needs the same operation.
{ "name": "forEachTask", "type": "flow/each", "to": { "default": { "setTaskStatus": ["default"] }, "empty": { "notifyNoTasks": ["default"] } }}Keep loop bodies small. If each item needs a complex process, move that process into a separate workflow and call it with action/run.
Selectors and field events
Section titled “Selectors and field events”Field events can provide a selector that points to the changed part of the form. Store it and use it when setting values back on the client.
{ "name": "selectChangedItem", "type": "op/selector", "set": { "fieldSelector": ":selector" }}{ "name": "setClientValue", "type": "action/set", "options": { "copy": null, "add": [], "recipes": { "description": "$selectedTemplate.description" }, "properties": { "validateMessage": { "${fieldSelector}.description": "null" } } }}Use this pattern for list items so the workflow updates the changed row instead of the entire form.
Data-flow checklist
Section titled “Data-flow checklist”- Store important intermediate values with clear names.
- Use explicit
inbindings when more than one resource is in context. - Keep recipes short and named by intent.
- Handle empty arrays before looping.
- Do not pass private service responses to the client unless the workflow intentionally sanitizes them.