Documentation Coming soon
Workflows
Conditional edges and bounded loops on the pipeline: branch on a verdict, retry with a cap, route on the result. Planned, coming soon.
Last updated
Workflows extend the pipeline with conditional edges and loops, so a run can branch on a result: “if toxic, rewrite and re-check, up to three times, then route”. They are planned and not live yet; this page describes the contract they will ship with. Every linear pipeline is a valid workflow, so nothing you build on chains today changes.
Billing is unchanged: every executed step bills the characters it processes. A node that runs five times is five billed steps. Conditions and routing are never billed, because neither runs a model.
Conditional edges
The pipeline array stays the primary structure. A node with no next continues to
the next item in the array, exactly as chains work today. Conditional flow is added,
never required.
A node may carry a next list of edges. The first edge whose when matches is taken;
if none match, execution continues to the next array item. goto names the target
node id, or "end" to finish the run.
{
"id": "tox",
"service": "security.toxicity.flag",
"next": [
{ "when": { "verdict": "flag" }, "goto": "soften", "max_iterations": 3 },
{ "goto": "intent" }
]
}
A when condition reads this node’s own result, using the same vocabulary as
stop_if: { "verdict": "flag" }, { "score_gte": 0.9 }, plus path matching for
extract results, such as { "path": "data.intent", "equals": "billing.dispute" }. An
edge with no when is unconditional and acts as the fallback.
Bounded loops
An edge that points to an earlier node, or to itself, must carry max_iterations: how
many times that edge may be taken in one run, with a hard ceiling of 1000. When an
edge has spent its iterations and would match again, on_max decides:
"skip"(default): the edge is ignored and the remaining edges are evaluated, so an unconditional fallback edge takes over."halt": the run stops withhalted_reason: "max_iterations".
A worked example
If a message is flagged toxic, rewrite the tone and re-check, at most three times;
otherwise route the intent. content.tone.rewrite is a forthcoming content service,
shown here because it is the natural fit.
{
"input": { "type": "text", "value": "..." },
"pipeline": [
{ "id": "shield", "service": "security.jailbreak.shield", "stop_if": { "verdict": "flag" } },
{ "id": "redact", "service": "governance.pii.redact" },
{
"id": "tox",
"service": "security.toxicity.flag",
"next": [
{ "when": { "verdict": "flag" }, "goto": "soften", "max_iterations": 3 },
{ "goto": "intent" }
]
},
{
"id": "soften",
"service": "content.tone.rewrite",
"next": [ { "goto": "tox" } ]
},
{ "id": "intent", "service": "routing.intent.map", "params": { "catalog": "support-v3" } }
],
"options": { "max_steps": 20, "max_ms": 5000 }
}
Worst case: shield, redact, tox, then up to three rounds of soften and tox, then intent via the fallback edge. Ten steps. If tox still flags after the third round, the loop edge is skipped and the fallback edge routes to intent.
Run-level limits
Workflows accept run-level options. Caps protect both sides from runaway runs.
| Option | Default | Ceiling | Notes |
|---|---|---|---|
max_steps |
100 | 1000 | Executed steps only |
max_ms |
30000 | 60000 | Wall clock for the whole run |
max_iterations |
required on backward edges | 1000 | Set on the edge, not the run |
Halting
Any cap hit or stop_if trigger returns status: "halted" with halted_at (the last
executed step), a halted_reason (stop_if, max_steps, max_ms, or
max_iterations), and the payload as it stood at the halt. Billing covers what ran.
Validation
Before any compute, the validator rejects runs that cannot succeed: unknown goto
targets, backward edges without max_iterations, type mismatches along any reachable
path, and worst-case step counts above the run’s max_steps. A rejected run never
runs, and never bills.
Billing and audit in workflows
The billing rule applies unchanged, counting executed steps. A node that executes five
times is five steps; repeated executions are keyed tox#2, tox#3, and so on in
results, usage.billed, and the audit trace, so each iteration is billed and
traced on its own.
The audit trace records iteration and edge_taken for each executed step: the
matched when and goto target, or "sequential". When a run halts, halted_reason
appears in the audit header.