Public beta — not for production use. Data may be wiped at any time. Questions? Contact us.
Documentation menu

Execution model

How Axiom runs a flow: immutable compiled artifacts, unary vs pipeline (SSE) invocation, the Executions list and detail pages, and durable resume after worker failure.

View as Markdown

Every run of a flow is an execution: the platform compiles the flow into an immutable compiled artifact, assigns an execution ID when the request is accepted, and records durable checkpoints as each node completes. A flow runs in one of two modes — unary (one input message in, one result out) or pipeline (a stream of frames delivered over Server-Sent Events) — and either way the run survives infrastructure failure: if a worker dies mid-run, a replacement resumes from the last checkpoint instead of starting over.

Flows run as compiled artifacts

Invoking a flow never executes an editable draft — it always executes a compiled artifact. Compilation validates that every edge connects type-compatible messages, resolves every node placement, fixes the entry node and terminal node, and freezes the execution mode (unary or pipeline) into the artifact. An artifact is immutable: editing the flow and compiling again produces a new artifact with a new ID.

The artifact ID is the graph_id you pass when invoking the flow over HTTP. In the editor, the flow inspector's API section shows the flow's invoke endpoint, and its Use via API (curl) action compiles the current canvas and produces a ready-to-paste curl command with the compiled graph_id filled in. That graph_id is pinned to the version of the flow that was compiled — after editing the flow, reopen the dialog to get an updated command. The same section's Open interactive docs button opens a live API reference generated for that exact artifact (see Use the interactive API docs).

Workers cache compiled artifacts by ID, so repeat invocations skip graph resolution entirely.

Unary invocation

A unary flow handles one input message and produces one result per invocation. This is the default mode for flows that contain no pipeline nodes. Invoke it with POST /invocations/v1/flows/invoke on your deployment's origin, authenticated with an API key created under Console → API Keys (the key must belong to the same account that owns the flow):

# Replace the origin and graph_id with the values from "Use via API (curl)"
curl -X POST 'https://flows.example-axiom-host.com/invocations/v1/flows/invoke' \
  -H "Authorization: Bearer $AXIOM_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"graph_id": "01JX3Z9KQ4WV8RT2M5XB7CDEFG", "input": {"name": "Ada"}, "wait": true}'

The request body fields:

  • input — a JSON object matching the entry node's input message. The platform converts it to protobuf for you; callers never deal with binary serialization.
  • wait — when true, the call blocks until the flow completes and the response carries result, with the terminal node's output decoded back to JSON in result.output. The default wait timeout is 30 seconds; set timeout_seconds to override it. When false, the flow runs asynchronously and only execution_id is returned.
  • webhook — optional {"url": "...", "headers": {...}}; for asynchronous runs the result is delivered by HTTP POST to that URL instead.
  • config_id — optional flow config profile; when omitted the default hierarchy applies (tenant default → flow default). See Flow configs.

A successful wait: true response (HTTP 202):

{
  "accepted": true,
  "execution_id": "0af7651916cd43dd8448eb211c80319c",
  "result": {
    "output": { "greeting": "Hello, Ada!" },
    "success": true,
    "completed_at": 1780000000
  }
}

The execution_id identifies this run everywhere — the Executions pages, the events API, and distributed traces all reference it.

Pipeline invocation streams frames over SSE

A pipeline node is a streaming generator: declared with type: pipeline in axiom.yaml, it emits a sequence of output frames instead of a single output message. A flow that contains any pipeline node always runs in pipeline mode — frames flow continuously between nodes, and results stream back to the caller progressively.

Pipeline flows compile to a pipeline artifact and must be invoked on the SSE endpoint — wait applies only to unary invokes:

# -N disables curl's buffering so frames render as they arrive
curl -N -X POST 'https://flows.example-axiom-host.com/invocations/v1/flows/invoke/stream' \
  -H "Authorization: Bearer $AXIOM_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"graph_id": "01JX3ZAB2PIPE8RT2M5XB7CDEF", "input": {"name": "Ada"}}'

The response is a Server-Sent Events stream (Content-Type: text/event-stream). Each event is one frame:

data: {"execution_id":"0af7651916cd43dd8448eb211c80319c","frame_index":0,"payload":{"greeting":"Hello, Ada!"},"is_final":false,"success":true}

data: {"execution_id":"0af7651916cd43dd8448eb211c80319c","frame_index":1,"is_final":true,"success":true}

payload is the flow output for that frame, decoded to JSON using the terminal node's output message. The stream ends when a frame arrives with is_final: true; on failure the final frame carries an error string instead of a payload. If no final frame arrives within the timeout (30 seconds by default, timeout_seconds to override), the stream closes with a final error frame.

Running a flow from the editor

The Run button sits at the bottom-center of the editor canvas and opens the Run Graph dialog. The dialog shows the flow's type — "Unary — single request / response" or "⚡ Pipeline — streams multiple frames via SSE" — with a Switch action to toggle between them. Switching is disabled when the flow contains a pipeline node, because a pipeline node's streaming frames cannot be delivered through a unary run.

Fill in the entry node's input fields and press ▶ Run. Unary runs also offer a Debug Mode toggle to watch the execution in real time node by node (see Debug a flow). Every run started from the editor is recorded as an execution, exactly like an API-triggered run.

The executions list

Click Executions in the top header to open /executions: one row per execution, newest first, with Status, Started, Duration, Flow, and Execution columns. Filter by status, by flow, or by date range. The same data is available over HTTP at GET /invocations/v1/executions.

An execution's status is one of:

StatusMeaning
QueuedAccepted, not yet picked up by a worker
RunningA worker is executing nodes
Paused (HITL)Waiting on a human-in-the-loop approval
Waiting on gateWaiting for parallel branches to converge at a gate
Debug pausedStopped at a breakpoint in a debug session
CompletedTerminal node finished; result available
FailedA node failed and the flow did not complete
CompensatingRunning compensation steps after a failure
CancelledStopped by an explicit cancel request

A running execution can be cancelled with DELETE /invocations/v1/flows/{execution_id}; cancellation takes effect immediately, even if the flow is blocked inside a long-running node.

Execution detail

Selecting an execution opens /executions/<execution-id>: a read-only canvas that replays the run on the flow's topology, a timeline scrubber to step through it, and a header showing the status plus Started, Updated, Completed, and Duration timestamps. Six tabs break the run down:

  • Timeline — the run's events in time order, driving the scrubber.
  • Events — the persisted per-node event log, filterable by node. Also available at GET /invocations/v1/executions/{id}/events.
  • Checkpoints — the durable checkpoint written after each completed node, including its recorded output. Also available at GET /invocations/v1/executions/{id}/checkpoints.
  • Pauses — human-in-the-loop pauses and how each was resumed.
  • Branches — gate activity for parallel branches.
  • Output — the flow's final output (or failure), decoded against the terminal node's output message.

The detail page is fed entirely from persisted history, so it renders the same run identically on every reload — including after the run finished.

Durable resume

After every completed node, the platform durably records a checkpoint containing that node's output before moving on. Checkpoints are what make an execution survive infrastructure failure: if the worker executing a flow dies mid-run, the platform redelivers the run to a replacement worker, which loads the checkpoint history, skips every node that already completed, and re-runs only the node that was in flight when the worker died. The execution keeps its ID and completes normally — callers waiting on a result or an SSE stream do not need to retry.

Checkpoints are retained for 30 days by default and are visible on the Checkpoints tab of the execution detail page. Large node outputs are stored out-of-band and rehydrated transparently when a resumed run (or the detail page) needs them.

Pauses are durable the same way: an execution in Paused (HITL) or Waiting on gate holds its state indefinitely — across worker restarts — until it is resumed or times out per its configured policy.