end node

Terminal node that sets the run's recorded outcome.

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 text

end 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 end for every path. Routing every transition to a single done end loses the distinction between successful and failed runs in history. Use multiple ends with different outcomes.
  • Using the wrong outcome. cancelled is for explicitly aborted runs (e.g. a human reject 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. end is terminal by construction.
  • Forgetting an end entirely. Nothing requires a workflow to reach an end node: a state with no on map at all also stops the run (recorded as completed), but with no outcome written. Give every terminal path an explicit end node so run history shows why it finished.
  • Treating message as structured data. message is 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.

View this page as Markdown

Predictable workflows from unpredictable AI