Destination triggers
When a destination fires — conditions on answers, confidence, weighted levels and composites, final actions, a minimum confidence per route, score levels, probability thresholds — and in which order.
Every destination has a when object. It fires when the decision's final action is one of when.actions (if you set any) and every condition in when.conditions holds:
{
"conditions": [
{ "field": "route", "on": "output", "operator": "eq", "value": "sales" },
{ "field": "route", "on": "confidence", "operator": "gte", "value": 0.8 }
],
"actions": ["continue"]
}| Property | Type | Default | Description |
|---|---|---|---|
conditions | array | [] | Up to 5 conditions, all of which must hold. Same shape as a policy condition: field, on (output, confidence or score), operator and value. |
actions | array | — | Fire only when the final action is one of these: continue, block, escalate, fallback. |
An empty when fires after every decision.
Per option
The most common trigger, and what the Destination selector of an option creates: the question's answer equals the option.
{ "conditions": [{ "field": "route", "on": "output", "operator": "eq", "value": "sales" }] }Use neq for "every route except spam". The reserved other option is an answer like any other: a destination on route = other fires when no option fits — while the decision's fallback action (escalate by default) tells your code that a person should look.
Minimum confidence per route
The Minimum confidence field of a destination adds a confidence ≥ condition on the same question, so the route only fires when the engine is sure enough:
{
"conditions": [
{ "field": "route", "on": "output", "operator": "eq", "value": "sales" },
{ "field": "route", "on": "confidence", "operator": "gte", "value": 0.8 }
]
}It is independent of the question's own minConfidence, which changes the action: a low-confidence answer gets the fallback action and action_reason.type = "low_confidence", whatever the destinations. Combine both, or give each route its own threshold — a refund route can demand 0.9 while a "send an article" route accepts 0.6. See confidence-gated routing.
Per score level
A score question's selectors create a condition on the most likely level:
{ "conditions": [{ "field": "priority", "on": "output", "operator": "eq", "value": "critical" }] }Levels can also be compared by number (1 = the first level) with any operator — "operator": "gte", "value": 3 is high or critical on a four-level scale — or by weighted level with "on": "score", which can land between levels: "on": "score", "operator": "gte", "value": 3.5.
Probability thresholds
Destination when … ≥ … on a probability question creates a threshold on the probability of yes, 0.8 by default:
{ "conditions": [{ "field": "purchase_intent", "on": "output", "operator": "gte", "value": 0.8 }] }Composites and other answers
A condition can use any question or composite of the decision, so a destination doesn't have to follow the question its panel belongs to: lead_score ≥ 0.75 sends hot leads to a workflow whatever the route.
{
"conditions": [
{ "field": "lead_score", "on": "output", "operator": "gte", "value": 0.75 },
{ "field": "route", "on": "output", "operator": "neq", "value": "spam" }
]
}Final actions
actions ties a destination to the verdict instead of an answer — every escalate posts to Slack, every block opens a moderation ticket:
{ "actions": ["escalate"] }With both conditions and actions, both must hold.
Validation
Conditions follow the rules of policy conditions, and they are checked when you save or deploy — 422 INVALID_SCHEMA otherwise:
- the
fieldmust be a question or a composite of the decision; - choice answers only support
eqandneq, and the value must be one of the options; - probabilities and confidence are numbers from 0 to 1 —
0.7, not70; - score levels go from 1 to the number of levels, or use a level label;
- composites go from −1 to 1 (0 to 1 with positive weights).
Evaluation
After the action is chosen, Dcision walks the destinations in schema order:
- A destination that is switched off (
"enabled": false) is ignored. - Its
actionsandconditionsare checked. Destinations that don't match leave no entry. - Its params are resolved. When a param marked
requiredhas no value, the destination doesn't fire: its entry says"status": "skipped", "reason": "missing_param"and names theparam. - At most 10 destinations fire per execution, and at most 3 of them can make the caller wait — LLM answers and agents in
syncmode. Beyond that, the entry says"status": "skipped", "reason": "limit". - The rest run: replies and functions are returned, LLM and
syncagent calls are awaited in parallel, and background deliveries are queued.
When the engine fails
With onEngineError: "fallback", an engine failure answers 200 with the fallback action and no answers. Then:
- destinations with conditions don't fire — there is no answer to compare;
- destinations without conditions do, filtered by
actionswhen set — the right place for an alert such as escalate → notify the on-call person; result.*,confidence.*,scores.*andcomposites.*variables arenull.
Fallback answers are retried for real
Engine-error fallbacks aren't stored for idempotency: retrying the call with the same Idempotency-Key runs the decision again, and its destinations fire again with new delivery IDs. Deduplicate such alerts on your side — for example by your own record ID.
Replays
An idempotent replay returns the stored response — including its destinations entries and their delivery IDs — and fires nothing again. The SDKs, though, dispatch the functions of a replayed response again, so make function handlers idempotent.
Destinations overview
What happens after a decision — per option, score level, probability threshold or final action — fixed replies, LLM answers, your agent, workflows, signed webhooks, HTTP requests and functions.
Params and templates
Map values into a destination — params from a field or a fixed value, required params, {{…}} variables in URLs, headers, bodies and texts, and how each place encodes them for you.