Marks a terminal state of the workflow and records the run's final
outcome. Every workflow needs at least one end node; most non-trivial
workflows have several: one per distinct way the run can finish.
Purpose#
Use end to give each terminal path a meaningful, queryable outcome. The
outcome you set here is what shows up in run history, what dashboards
aggregate over, and what callers (CLI, web UI, MCP, REST) inspect to know
whether the run "succeeded".
YAML schema#
states:
done:
type: end # discriminator
label: Done # required, shown in UI / logs
outcome: success # required: success | failure | cancelled
message: Reviewer quorum agreed # optional, free textend nodes have no on map. They are terminal by construction.
Configuration#
| Field | Type | Required | Default | Meaning |
|---|---|---|---|---|
type |
'end' |
yes | — | Discriminator. |
label |
string | yes | — | Human-readable name for the terminal state. |
outcome |
'success' | 'failure' | 'cancelled' |
yes | — | Recorded on the run; drives history and aggregations. |
message |
string | no | — | Optional context shown alongside the outcome in the UI. |
A workflow may have any number of end nodes: one per distinct terminal
path. It is conventional (but not required) to have at least one
success end and at least one failure end.
Outputs#
| Key | Type | Notes |
|---|---|---|
outcome |
string | Echo of the configured outcome. |
message |
string | Echo of the configured message, if any. |
These are visible in run inspection but, since end is terminal, no node
will read from them downstream.
Transitions#
None. end is terminal, so it has no on map, and the runner finalizes the
run as soon as an end node completes.
Worked example#
name: ai-judge
version: 1
description: Single-agent gate with separate ends for yes and no.
initial: ask
states:
ask:
type: ai_judge
label: Is the sky blue?
model: claude-haiku-4-5
prompt: |
Is the daytime sky on a clear day blue? Answer with the JSON shape
requested below.
validator:
kind: boolean
on:
'true': yes-end
'false': no-end
yes-end:
type: end
label: Yes
outcome: success
message: The judge agreed.
no-end:
type: end
label: No
outcome: failure
message: The judge disagreed.Common pitfalls#
- Reusing one
endfor every path. Routing every transition to a singledoneend loses the distinction between successful and failed runs in history. Use multiple ends with different outcomes. - Using the wrong outcome.
cancelledis for explicitly aborted runs (e.g. ahumanreject path that genuinely shouldn't count as a failure). Don't reach for it to soften the look of a failed run. Metrics and dashboards depend on the distinction. - Adding an
on:map. Schema rejects it.endis terminal by construction. - Forgetting an
endentirely. Nothing requires a workflow to reach anendnode: a state with noonmap at all also stops the run (recorded ascompleted), but with nooutcomewritten. Give every terminal path an explicitendnode so run history shows why it finished. - Treating
messageas structured data.messageis free text shown in the UI. If you need structured information about the outcome, write it from an upstream node into the run's variables instead.