Event Script flow grammar
At a glance
- The source of truth for the Event Script flow DSL — the rules an author (human or AI) needs to write a valid flow without inferring from examples.
- Machine-readable form:
event-script-flow.json. Authoring from an agent? See the AI agent guide. Tutorial/worked examples: Event Script Syntax; exhaustive field docs: Flow Schema Reference.- A flow is compiled and validated by the engine; the rules below mirror that validation (
CompileFlows), so violating them fails compilation.
Flow file structure
A flow is a YAML file with these top-level keys:
| Key | Required | Meaning |
|---|---|---|
flow.id |
yes | unique flow id (referenced by rest.yaml and by sub-flows as flow://{id}) |
flow.description |
yes | human-readable purpose (validated non-blank) |
flow.ttl |
yes | time-to-live, e.g. 30s (s/m/h; minimum 1s) |
flow.exception |
no | route of a global exception handler |
first.task |
yes | the entry-point task (route name or task name) |
external.state.machine |
conditional | route or flow://id of an external state machine — required if any task uses the ext: namespace |
tasks |
yes | the list of task definitions; must include ≥1 task with execution: end |
Task fields
Each entry under tasks: may have:
| Field | Required | Meaning |
|---|---|---|
process |
conditional | route of the composable function, or flow://{flow-id} for a sub-flow (either process or name required) |
name |
conditional | unique task id; defaults to process — required only when the same process is used more than once |
description |
yes | non-blank purpose |
input |
yes | list of source -> target mapping rules (use [] for none) |
output |
yes | list of source -> target mapping rules (use [] for none) |
execution |
yes | one of the execution types below |
next |
conditional | next task(s) — required/shaped by execution type |
join |
conditional | the join task — required for fork |
pipeline |
conditional | ordered task list — required for pipeline |
loop |
no | loop control (statement/condition) for a pipeline |
source |
no | a model.* list to iterate for a dynamic fork |
delay |
no | ms (int) or a model.* var; must be < flow.ttl |
exception |
no | task-level exception handler route (overrides flow.exception) |
Execution types
The eight values execution: may take (authoritative set —
CompileFlows.EXECUTION_TYPES). next requirements are enforced at compile time:
execution |
next |
also requires | forbids | does |
|---|---|---|---|---|
sequential |
exactly 1 | — | — | run, then go to the one next task |
decision |
≥ 2 | output maps -> decision |
join,pipeline,source |
branch on the decision value (false→next[0]/true→next[1]; or numeric 1-indexed) |
parallel |
≥ 2 | — | join |
fan out to all next tasks concurrently |
fork |
≥ 1 | join |
pipeline |
run next tasks concurrently, resume at join; optional source to iterate |
pipeline |
exactly 1 | pipeline list |
join |
run the pipeline tasks in order (optional loop), then the one next task |
response |
exactly 1 | — | join |
send the HTTP response now, then continue async to next |
end |
none | — | next,join,… |
terminate; output mappings form the final response |
sink |
none | — | next,… |
terminal branch (no response) — e.g. a fork/parallel leaf |
Data-mapping mini-language
Every input/output entry is 'source -> target' (one -> per rule; a three-part
'LHS -> model.var -> RHS' is allowed and compiles to two).
Source (LHS) namespaces
| Namespace | Use |
|---|---|
input.* |
the HTTP request dataset (input.body, input.header.*, input.query.*, input.path_parameter.*, input.method, …) |
model.* |
flow-instance state (dot/bracket/{model.key} dynamic keys) |
model.parent.* / model.root.* |
parent-flow state (in sub-flows) |
error.* |
exception context in a handler (error.task/.status/.message/.stack) |
$.… |
a JSONPath expression |
result / input / header / status / datatype |
(in output rules) the function's result, the task input, response headers, status code, or result class name |
| constants | text(…), int(…), long(…), float(…), double(…), boolean(…), map(k=v,…), file(text:/json:/binary:path), classpath(…) |
f:fn(args) |
a simple plugin function |
Target (RHS) namespaces
| Namespace | Use |
|---|---|
output.* |
the HTTP response (output.body, output.header.*, output.status) |
model.* (+ model.parent.*) |
store in flow / parent state ([] appends to a list) |
decision |
(in a decision task) the branch selector |
file(path) / file(append:path) |
write/append a file |
ext:… |
external state machine (needs external.state.machine) |
Input vs. output targets (this trips up authors). In an input mapping the right-hand side
is the function's input body — written with no namespace (e.g. model.order -> order puts
order into the function's input body), or header.* for input headers. In an output mapping
the right-hand side uses the namespaces above. On the left of an output mapping, result
(bare) is the whole function result, result.x a field, and status/header/datatype other
function outputs. model.* always needs a specific key — no whole-model access.
Type-conversion suffixes (on a source): :text :int :long :float :double :boolean
:binary :b64 :! (negate) :uuid :length :substring(a[,b]) :concat(…) :and(model.k)
:or(model.k). Special tokens: -> (map), [] (append), * (whole-object map),
{model.key} (runtime interpolation in text()), .ITEM/.INDEX (dynamic-fork iteration).
The full catalog with examples is in Event Script Syntax.
Triggering, chaining & exceptions
- Tasks are referenced — in
first.task,next,join, andpipeline— by theirname(which defaults toprocess). - The engine runs
first.task, then chains by execution type (next/join/pipeline). - A
decisiontask routes by the value itsoutputmaps intodecision. flow.exceptioncatches unhandled task errors; a task's ownexceptionoverrides it; the handler readserror.*. The built-inresilience.handleradds retry/backoff.
Invariants
Compile-time rules — violate them and the flow won't load:
flow.id,flow.description,flow.ttl,first.task, andtasksare all present;ttl ≥ 1s.- Every task has
description,input,output, and a validexecution. - There is ≥1
endtask. decisionneeds ≥2next;sequential/response/pipelineneed exactly 1;end/sinkhave nonext.forkrequiresjoin;pipelinerequires apipelinelist.- A
nameis required when the sameprocessappears in more than one task. ext:targets requireexternal.state.machineto be declared.
See also
event-script-flow.json— machine-readable form of this grammar.- AI agent guide — authoring flows deterministically from an agent.
- Event Script Syntax · Flow Schema Reference — worked examples and exhaustive field docs.