#Policy chains
Single policies handle single decisions. Chains turn the engine
into a decision pipeline: each step's outcome flows into the next
step's inputs, letting you compose classify → score → decide → generate workflows from individually-versioned policies.
#Why chain instead of one mega-policy
- Each step is independently authored, tested, and rolled back.
- The audit log shows the whole chain — operators can see exactly why the engine arrived at the final outcome.
- You can A/B test individual steps without touching the others.
- Different modes compose naturally: classify the input, score the classified result, decide an action from the score, generate the communication that explains the decision.
#Request body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| chain | array<ChainStep> | Yes | — | Ordered list of steps to run. 1–10 entries. Each step picks a policy slug and the mode to invoke it in. |
| inputs | object | No | {} | Initial input bag. Available to every step. Each step also sees a chain array (full history of prior outcomes) and a previous shortcut (last step's outcome only) injected into its inputs automatically. |
| context | object | No | {} | Side-channel metadata. Same semantic as on the single evaluate call. |
| halt_on | "error" | "never" | No | "error" | "error" halts the chain at the first non-ok step (default; halted_at tells you which step). "never" runs every step regardless — useful when later steps can still produce useful output even if earlier ones errored. |
ChainStep object:
{
"policy": "itsm/incident-classify",
"mode": "classify",
"version": null // optional — pin a specific version for replay
}
#Chain history available to each step
When a step runs, its inputs bag is the original request inputs
plus two automatic fields:
inputs = {
...originalInputs,
"chain": [
{ "slug": "step-1-policy", "mode": "classify", "outcome": {...} },
{ "slug": "step-2-policy", "mode": "score", "outcome": {...} }
],
"previous": {...} // shortcut for the most recent step's outcome
}
Policies reference these in their English text directly. A decide
policy might read: "Given the classification from previous.primary_label
and the score from chain[1].outcome.score, pick the right
routing action."
#Response
{
"request_id": "uuid",
"steps": [
{ "policy": "itsm/incident-classify", "version": 1, "mode": "classify",
"status": "ok", "outcome": {...}, "citations": [], "latency_ms": 1500 },
{ "policy": "itsm/incident-score", "version": 1, "mode": "score",
"status": "ok", "outcome": {...}, "citations": [], "latency_ms": 1500 },
{ "policy": "itsm/incident-route", "version": 1, "mode": "decide",
"status": "ok", "outcome": {...}, "citations": [], "latency_ms": 1200 }
],
"final": { ... }, // alias for steps[-1].outcome when chain completed
"halted_at": null, // step index where chain stopped, null when complete
"latency_ms_total": 4200,
"aggregate": { ... } // see Score aggregation
}
The aggregate block is documented separately in
Score aggregation.
#Example: ITSM incident triage chain
curl -X POST https://aiengine.velgent.com/api/v1/policies/chain \
-H "Authorization: Bearer velgent_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"chain": [
{ "policy": "itsm/incident-classify", "mode": "classify" },
{ "policy": "itsm/incident-score", "mode": "score" },
{ "policy": "itsm/incident-route", "mode": "decide" },
{ "policy": "itsm/customer-comms", "mode": "generate" }
],
"inputs": {
"short_description": "Payment service returning 500 errors",
"description": "~5% of transactions failing since 10:15am",
"affected_ci": "payment-svc-prod-01"
}
}'
In one call: classify the incident category, score its impact, decide the routing action, and draft the customer-facing comms — each step reading from the previous step's outcome. Total latency typically 6–8 seconds for a four-step chain.
Each chain run writes ONE audit_logs row with
operation: "policy_chain" plus one
policy_evaluations row per resolved step. All share
the same request_id, so a single ID drills into the
full chain trace.
Next: Graphs → — when some of your steps are independent and can run in parallel.