Skip to content

Overview

Workflows are directed graphs stored as JSON. A workflow starts from an event, passes data between elements, and ends after the connected elements have run. Each workflow should perform one clear task in the business logic.

A workflow file is usually stored in systems/<system-name>/workflows/<Identifier>.workflow.json.

{
"identifier": "CreateProjectTask",
"title": "Create project task",
"async": false,
"entry": "createProjectTask",
"elements": [],
"events": []
}
PropertyPurpose
identifierStable workflow identifier. Usually matches the file name without .workflow.json.
titleHuman-readable title for editors, logs, and notifications.
descriptionOptional description of the workflow intent.
asynctrue queues the workflow for background execution; false runs it immediately.
queueOptional queue name for asynchronous work.
debugEnables extra debugging behavior where supported.
entryName of the element that starts the workflow.
elementsArray of workflow nodes.
eventsEvent registrations that make the workflow callable from Brezel.

Elements are the nodes of a workflow graph. Every element has a name, a type, optional options, and optional connection or data-binding properties.

{
"name": "loadProject",
"type": "source/entities",
"options": {
"module": "projects",
"first": true
},
"set": {
"project": "default:"
},
"to": {
"default": {
"createTask": ["default"]
}
}
}

Element types use the format <category>/<type>.

CategoryTypical purpose
event/*Start or resume a workflow from a Brezel event.
source/*Load or create data.
op/*Transform, select, merge, or calculate data.
query/*Add query constraints, often for policies and scopes.
flow/*Branch, loop, or control execution.
action/*Persist data, respond to the client, send mail, export, or run another workflow.
api/*Communicate with external APIs.
cast/*Send live updates such as progress notifications.

See Element Types for the broader inventory.

Elements communicate through ports. Most elements expose a default output port, and many expose additional ports such as else, empty, error, confirm, or event.

Connections live in the to property:

{
"name": "hasProject",
"type": "flow/if",
"options": {
"condition": "$project != null"
},
"to": {
"default": {
"createTask": ["default"]
},
"else": {
"notifyMissingProject": ["default"]
}
}
}

The first key under to is the output port of the current element. The nested object maps target element names to their input ports.

Use set to store element output in the workflow context.

{
"name": "openTaskModal",
"type": "action/modal",
"options": {
"type": "confirm",
"layout": "tasks.modal",
"checkpoint": true
},
"set": {
"taskData": "confirm:"
}
}

The binding "taskData": "confirm:" stores the data emitted from the confirm port in $taskData.

Use in to pass a specific context value into an element input:

{
"name": "saveTask",
"type": "action/save",
"in": {
"default": "$task"
}
}

See Data Flow for more detail on context values, selectors, and recipes.

Events make a workflow callable by the UI, module lifecycle, cron scheduler, or policy system.

{
"entry": "createProjectTask",
"events": [
{
"identifier": "createProjectTask",
"type": "webhook",
"module": "projects"
}
],
"elements": [
{
"name": "createProjectTask",
"type": "event/webhook",
"options": {
"module": "projects",
"refresh_prototype": true
},
"set": {
"project": "default:",
"data": "input:"
}
}
]
}

The event registration and entry element usually share the same identifier. See Events and Triggers for common event types.

Synchronous workflows run in the current request. Use async: false when the workflow must return a response to the browser, open a modal, set client fields, redirect, or block a save operation.

Asynchronous workflows are queued and handled in the background. Use async: true for long-running work, exports, mail batches, imports, API polling, build jobs, and other tasks that should not keep the request open.

Good defaults:

  • Use sync for UI interactions and immediate validation.
  • Use async for work that may take more than a few seconds.
  • Use cast/progress for background jobs that should stay visible to the user.
  • Use a small sync workflow to validate and enqueue a larger async workflow with action/run.
  • Use PascalCase for workflow identifiers and file names, for example CreateProjectTask.workflow.json.
  • Use readable camelCase element names such as loadProject, createTask, and saveTask.
  • Prefer business names over generated names when editing a workflow manually.
  • Keep JSON formatted with 4 spaces and no trailing commas.
  • Keep each workflow focused on one business outcome.
  • Give branches and variables neutral, explicit names: $project, $taskData, $selectedCustomer.