Skip to content

Workflow Settings Schema

A workflow definition is stored as a WorkflowSettings document containing a version string and an ordered array of Node objects. This page is the reference for the raw JSON shape of workflow definitions — useful when building integrations, writing toolkit scripts, or constructing workflows programmatically.


WorkflowSettings

The top-level container for a workflow definition.

Field Type Required Description
version string No Optional version tag for your own change tracking
nodes Node[] Yes Ordered array of all nodes in the workflow
{
  "version": "1.0",
  "nodes": []
}

Workflow definitions are stored under settings.model.events for component-level workflows, or under extensions[].workflows for extension-level workflows.


Node

Each node represents a single step in the workflow graph.

Field Type Required Description
id number Yes Unique integer identifier within this workflow
name string Yes Human-readable label; also used as the event name for event type nodes
type string Yes Node category — see Node Types
action string Yes Execution action — typically matches type for operation nodes
component string No Component reference (slug or UUID) the node operates on
data NodeData Yes Type-specific configuration — see NodeData
connections NodeConnection[] Yes Outgoing connections to downstream nodes
parallel boolean No Whether connected child nodes execute concurrently (default: false)
paused boolean No When true, this node (and a recycle trigger) is skipped during execution
x number No Canvas x-coordinate (visual only)
y number No Canvas y-coordinate (visual only)

Node Types

type Category Description
event Trigger Listens for a model CRUD event and starts the workflow
recycle Trigger Timer-based trigger; runs on a fixed schedule
expression Operation Executes a JavaScript expression in the sandbox
condition Control Flow Branches based on a boolean expression
switch Control Flow Fans out across multiple named paths
each Control Flow Iterates over a collection, executing children per item

NodeData

NodeData is a free-form object whose keys vary by node type.

Event node

{
  "type": "event",
  "name": "Create",
  "data": {}
}

The event name on the name field determines which model event triggers this node. For multi-schema components prefix the schema name:

Create               → default schema
premium::Create      → premium schema
Before::Update       → default schema, synchronous

Recycle node

{
  "type": "event",
  "action": "recycle",
  "name": "Daily job",
  "data": {
    "timer": "1d",
    "ruleset": "active = true"
  }
}
Field Type Description
timer string Schedule interval — see available values
ruleset string Optional filter expression; only matching records are loaded

Expression node

{
  "type": "operation",
  "action": "expression",
  "name": "Process Order",
  "data": {
    "expression": "io.pipe({ id: $models[0].uuid })"
  }
}
Field Type Description
expression string JavaScript code executed in the sandbox

Condition node

{
  "type": "condition",
  "action": "condition",
  "name": "Is Active?",
  "data": {
      "expression": "if ($models[0].active) { resolver.resolve() } else { resolver.reject() }"
  }
}
Field Type Description
expression string JavaScript code that calls resolver.resolve() or resolver.reject()

Switch node

{
  "type": "switch",
  "action": "switch",
  "name": "Route by Type",
  "data": {
      "expression": "follow.path($models[0].type)"
  }
}
Field Type Description
expression string JavaScript code that calls follow.path(name) one or more times

Each node

{
  "type": "each",
  "action": "each",
  "name": "For Each Item",
  "data": {}
}

The each node does not require any data fields. It reads the collection provided by io.each(array) from its upstream node and executes child nodes once per item.


NodeConnection

Connections define directed edges between nodes.

Field Type Required Description
id number Yes Unique integer identifier for this connection
source ConnectionPoint Yes The output slot on the source node
destination ConnectionPoint Yes The input slot on the destination node
type string Yes Connection type — "pass", "reject", or a named switch path
name string \| null No Human-readable label (optional)

Connection types

type Used by When followed
pass All nodes, Condition (resolve) Default forward path
reject Condition resolver.reject() was called
<path name> Switch follow.path(name) was called with this name

ConnectionPoint

Field Type Description
id number Matches the id of the source or destination node
position string Visual attachment side — "top", "bottom", "left", or "right"

Complete Example

A workflow that runs on Create, checks if the order is above a threshold, and either sends a notification or logs a skip:

{
  "version": "1.0",
  "nodes": [
    {
      "id": 1,
      "name": "Create",
      "type": "event",
      "action": "event",
      "data": {},
      "connections": [
        {
          "id": 10,
          "source":      { "id": 1, "position": "bottom" },
          "destination": { "id": 2, "position": "top" },
          "type": "pass",
          "name": null
        }
      ],
      "parallel": false,
      "paused": false,
      "x": 100,
      "y": 50
    },
    {
      "id": 2,
      "name": "High Value?",
      "type": "condition",
      "action": "condition",
      "data": {
        "expression": "if ($models[0].total > 1000) { resolver.resolve() } else { resolver.reject() }"
      },
      "connections": [
        {
          "id": 11,
          "source":      { "id": 2, "position": "bottom" },
          "destination": { "id": 3, "position": "top" },
          "type": "pass",
          "name": null
        },
        {
          "id": 12,
          "source":      { "id": 2, "position": "right" },
          "destination": { "id": 4, "position": "top" },
          "type": "reject",
          "name": null
        }
      ],
      "parallel": false,
      "paused": false,
      "x": 100,
      "y": 200
    },
    {
      "id": 3,
      "name": "Notify Team",
      "type": "operation",
      "action": "expression",
      "data": {
        "expression": "await Component('notifications').create({ type: 'high_value_order', ref: $models[0].uuid })"
      },
      "connections": [],
      "parallel": false,
      "paused": false,
      "x": 0,
      "y": 380
    },
    {
      "id": 4,
      "name": "Skip",
      "type": "operation",
      "action": "expression",
      "data": {
        "expression": "return"
      },
      "connections": [],
      "parallel": false,
      "paused": false,
      "x": 200,
      "y": 380
    }
  ]
}

Node IDs

Node id values must be unique integers within a single workflow definition. They are used by NodeConnection to wire source and destination. There is no required ordering or continuity — you can use any integers as long as they are unique.