Skip to content

Workflow File Format ​

This page documents the JSON shape of a workflow: every top-level field, what a node and a connection look like on the wire, and which fields are required. It assumes you already know what a workflow, node, and connection are; see Concepts first if not.

The file itself ​

A workflow is plain JSON, saved with a .aerini extension by convention. A .json file with the same shape works the same way, see Workflow file. This is the shape Aerini's own database stores, the shape exchanged between the desktop frontend and the engine, and the shape the scheduler loads for a background run.

JSON Schema ​

A JSON Schema (draft 2020-12) for this shape is generated from the engine's own types and kept in the repository as docs/reference/workflow.schema.json. Use it to check a hand-written or generated file before you import it. It checks field names, value types, and which fields are required, the same things the tables below describe. It does not check that a node_type_id exists, that a node's config is valid for that node, or that connections fit a node's ports; the Node Catalog lists what each node accepts. The schema describes the current schema version.

Top-level fields ​

FieldTypeRequiredDefault / notes
schema_versionstringNoSee Schema version below.
idstringYesUnique ID for this workflow.
namestringYesDisplay name shown in the Workflows sidebar and window title.
descriptionstringNoDefaults to an empty string. Not currently editable from the desktop UI; present in the schema for hand-authored or generated files.
nodesarray of node objectsYesEvery box on the canvas, including trigger nodes.
edgesarray of connection objectsYesCalled a "connection" everywhere else in these docs; the JSON key is edges.
metadataobjectNoDefaults per field if omitted, see Metadata below.
max_duration_secsnumberNoWall-clock limit for the whole run, in seconds. The executor clamps any value to the 10 to 86400 range. Omit for no limit.
unlimited_durationbooleanNoDefaults to false. Only takes effect on a manual run started from the desktop app; scheduled and background runs never read this field. When true, the run ignores both the server's duration ceiling and this workflow's own max_duration_secs.
parallel_executionbooleanNoDefaults to false. When true, independent branches run concurrently instead of one after another.
max_concurrent_nodesnumberNoCaps simultaneous node tasks when parallel_execution is true. Falls back to 8 if unset.
settingsobjectNoCurrently holds only chat, see Chat settings below.

Metadata ​

The metadata object as a whole is optional: leave it out entirely and every field below falls back to its default. Include it, though, and author, created_at, and updated_at are required inside it; only tags and collection_id keep their own per-field defaults regardless.

FieldTypeDefault when metadata is absent
authorstring"user"
created_atstring, ISO-8601current time
updated_atstring, ISO-8601current time
versionstring"1.0.0". A free-form label, not the same field as the top-level schema_version.
tagsarray of strings[]
collection_idstring or nullnull

collection_id references a named collection in the Workflows sidebar by ID. The collection's own name and color live outside the workflow file; a missing or null collection_id leaves the workflow Uncategorized.

The desktop app cleans tags when it opens a workflow: entries that are not strings or are empty after trimming are dropped, tags that differ only by letter case count as one (the first spelling is kept), a tag is cut to 32 characters, and only the first 20 are kept. The file on disk is not rewritten until the workflow is saved.

Chat settings ​

settings.chat mirrors the Chat Panel toggles set from the Workflow Settings modal. Each field defaults independently, so a file that sets only some of them still loads correctly. See Chat Panel for what each toggle actually does in the app.

FieldTypeDefault
allow_attachmentsbooleanfalse
allow_image_responsesbooleantrue
max_message_lengthnumber2000
session_persistencebooleantrue
show_brandingbooleantrue

Nodes ​

Aerini has no separate top-level list for triggers. Manual Trigger, Schedule, and Webhook are ordinary entries in nodes, identified by the node_type_id values manual_trigger, schedule, and webhook.

FieldTypeRequiredDefault / notes
idstringYesUnique within this workflow, e.g. "node_abc123".
node_type_idstringYesWhich node implementation to run, e.g. "http_request". See Nodes Reference for every valid value.
node_typestringYesOne of "action", "ai", "logic", "utility". Controls which palette section the node appears in; has no effect on execution.
namestringYesLabel shown on the canvas block.
configobjectNoDefaults to {}. Node-specific settings from the config panel; shape depends entirely on node_type_id, see Nodes Reference.
credentialsobjectNoDefaults to {}. Maps a config field name to a saved credential's ID, e.g. {"api_key": "openai-prod"}. The secret itself never appears in this file, see Credentials.
input_schemaobjectYesJSON Schema the node's input must match. The app always writes {} for a node with no schema of its own; the key still has to be present.
output_schemaobjectYesSame rule as input_schema, for the node's output.
retryobjectNoDefaults to one attempt, no automatic retry, see Retry below.
fallback_nodestring or nullNoReserved for future use. Not read by the executor in the current version.
disabledbooleanNoDefaults to false. When true, the executor skips the node and treats it as if it succeeded with empty output.
positionobjectNoDefaults to {"x": 0, "y": 0}. Canvas coordinates, {"x": number, "y": number}, ignored by the executor.

The desktop app also writes ports and dynamic_ports into every saved node object, canvas metadata it uses to draw connection points. Neither is read by the engine; a file that leaves them out loads fine.

Retry ​

FieldTypeDefault
max_attemptsnumber1
backoff_msnumber500

max_attempts: 1 means no automatic retry. A node with side effects, an email send or an HTTP POST, only retries on its own if a workflow author raises this above 1 from the Retry UI.

The engine clamps max_attempts to between 1 and 10 when it runs a node, and the Retry attempts field in the desktop app stops at 10 to match.

Connections ​

A connection is the line drawn from one node's output to another's input; the JSON key for the array holding them is edges, and each entry below is one connection.

FieldTypeRequiredDefault / notes
idstringYesUnique ID for this connection.
from_nodestringYesSource node's id.
from_portstringYesOutput port name on the source node, e.g. "output", or a branch name such as "on_true" for a node with more than one output.
to_nodestringYesTarget node's id.
to_portstringYesInput port name on the target node, e.g. "input". At most one connection may end on a given input port unless that port is multi-input; see Input port arity.
conditionstring or nullNoExpression the canvas uses to decide whether to draw the line. Not evaluated by the executor: routing follows from_port alone.
on_successstring or nullNoAlternate target node ID activated when the source node succeeds, used to register an extra reachability edge.
on_failurestring or nullNoTarget node ID the executor routes to when the source node fails after every retry attempt, instead of aborting the run. The target runs only when the route fires, after the source node, and does not need a connection from it. It cannot be a node that runs before the source: that is a cycle and the run fails with CycleDetected.

Input port arity ​

An input port accepts one connection unless its node type declares it multi-input; Merge's input is the only built-in one. Any number of connections may leave an output port. A node type marks a multi-input port with "arity": "multi" on the port in its descriptor; ports without arity accept one connection. The desktop app copies descriptor ports into each saved node's ports, but the engine reads arity from the node type, not from the file.

Before any node runs, the engine rejects a workflow that puts two or more connections on a single-input port and returns PortArityViolation: Input port 'input' on node 'n_c' accepts one connection but has 2; remove the extra wires or route them through a Merge node. Only the first violation, in edges order, is reported. The desktop app opens such a file with every connection intact, marks the port in red, and warns once; Run then fails with the error above.

Schema version ​

Every workflow file carries a schema_version field so whatever loads it knows which shape to expect. As of Aerini 0.4.0 the format hasn't changed since it was introduced, so this is "1.0" everywhere and there is nothing to migrate yet. A file with no schema_version field at all, saved before the field existed, is treated as the oldest version this build knows and loads normally.

What happens for a version that's older, newer, or otherwise mismatched, and what runs when a migration path actually exists, is covered in Updating §Opening older or newer workflow files rather than repeated here. A version-by-version history of format changes will live in schema-migrations.md once there is more than one version to show.

aerini-server's exported config ​

Choosing Export → Export for Server writes a config file, aerini-server.json, whose workflow_json field holds this exact schema, unmodified, as a JSON string, the same shape the desktop app already has saved to its own database. aerini-server serve reads that file and loads the workflow the same way the desktop app does. See Server Deployment and Server CLI Reference for the rest of that config file and the flags around it.

What Export writes ​

Export → Export workflow and Export → Export All write this same schema, field for field, with one deliberate exception: metadata.collection_id is always written as null. Collection membership is this sidebar's local folder organization, not a portable property of the workflow itself, so it never leaves the app through an exported file.

Everything else, including parallel_execution, unlimited_duration, max_concurrent_nodes, settings, and the rest of metadata, is written exactly as it stands in the workflow being exported, omitted only where Top-level fields above already says the native format omits an at-default field. Re-importing an exported file restores all of it; the workflow lands in Uncategorized, since collection membership is the one thing an export never carries.

A .json file works the same as a .aerini file here too, see Workflow file.

What's next ​

  • Concepts, for what a workflow, node, and connection are before reading their JSON shape
  • Nodes Reference, for what belongs inside a given node type's config
  • Credentials, for how the credentials map resolves to a real secret at run time
  • schema-migrations.md, for a version-by-version history once the format has more than one version to show