Policies and actions
How policy rules with AND conditions are written and evaluated, and how the action and action_reason of every response are computed — policy, other option, low confidence, default or engine error.
Questions produce answers; policies turn answers into an action your code can switch on. Every successful response carries both the action and the action_reason that explains it.
Actions
| Action | Meaning by convention |
|---|---|
continue | Proceed with the automated path. |
block | Stop: reject, drop or quarantine the event. |
escalate | Hand over to a person or a higher tier. |
fallback | Take your fallback path — for example a larger model or more retrieval. |
Actions are labels for your code. Dcision never blocks or reroutes anything by itself: a decision whose action is block still answers 200 OK, and your application applies the action.
Policy rules
A rule is one condition, optional and conditions, and the action to return when all of them hold:
{ "field": "route", "on": "output", "operator": "eq", "value": "spam", "action": "block" }| Property | Type | Default | Description |
|---|---|---|---|
field | string | — | The key of a question — or of a composite — in this decision. |
on | string | output | What to compare: the answer (output), its confidence, or the weighted level of a score question (score). |
operator | string | — | eq, neq, gt, gte, lt or lte. |
value | string or number | — | The value to compare with (strings up to 255 characters). |
and | array | — | Up to 5 more conditions — each with its own field, on, operator and value — that must also hold. |
action | string | — | continue, block, escalate or fallback. |
A decision has up to 50 rules. What value and operator accept depends on the field and on on:
| Field | on | Compares | value | Operators |
|---|---|---|---|---|
| choice question | output | the chosen option (case and surrounding spaces ignored) | an option, as a string | eq, neq |
| score question | output | the most likely level (1 = lowest) | a level number or a level label | all six |
| score question | score | the weighted level, from 1 — it can fall between levels, such as 2.4 | a number | all six |
| probability question | output | the probability of yes | a number from 0 to 1 | all six |
| any question | confidence | the answer's confidence | a number from 0 to 1 | all six |
| composite | output | the composite's value | a number | all six |
Rules that break these constraints make the schema invalid (422 INVALID_SCHEMA), for example "Choice outputs only support eq / neq.", "Unknown question \"intent\".", "Only score questions have a weighted level." or "Compare a composite's value with a number.".
AND conditions
Compound rules are what most patterns need. Every condition of a rule must hold for the rule to match. From the templates:
| Template | Rule |
|---|---|
ticket-triage | category = bug_report AND bug_severity ≥ 4 → escalate |
voice-banking | intent = approve_transfer AND confidence of intent < 0.9 → escalate |
customer-service-router | intent = technical_issue AND weighted level of complexity ≥ 2.5 → escalate |
resume-screening | senior_ic < 0.35 AND eng_manager < 0.35 → block |
As JSON, the first one reads:
{
"field": "category",
"on": "output",
"operator": "eq",
"value": "bug_report",
"and": [{ "field": "bug_severity", "on": "output", "operator": "gte", "value": 4 }],
"action": "escalate"
}Conditions of one rule can use different questions and composites, or the same question twice — its answer and its confidence, as in voice-banking. For OR, write two rules with the same action.
How the action is computed
After the engine answers, Dcision computes the composites, then walks four steps and stops at the first that applies:
Policies, in order. The first rule whose conditions all hold sets the action.
action_reason → { "type": "policy", "rule": 1 } — rule is the 0-based index of the rule (the app shows it as "Policy rule #2").
A choice answered other. The first choice question, in question order, whose answer is the reserved other option triggers the decision's fallback action (settings.fallbackAction, escalate by default).
action_reason → { "type": "other_option", "question": "route" }
Low confidence. The first question, in question order, whose confidence is below its minConfidence triggers the fallback action.
action_reason → { "type": "low_confidence", "question": "route", "confidence": 0.52, "minConfidence": 0.8 }
Default. Nothing applied: the action is continue.
action_reason → { "type": "default" }
Policies always win: a rule such as route eq other → block takes precedence over the fallback action.
A fifth reason exists outside these steps. When the engine fails and the decision's onEngineError is fallback, there are no answers to evaluate: the response carries the fallback action and { "type": "engine_error", "code": "ENGINE_TIMEOUT" }.
Worked example
The Lead Qualification template has two rules and the default fallback action, escalate:
[
{ "field": "route", "on": "output", "operator": "eq", "value": "spam", "action": "block" },
{ "field": "purchase_intent", "on": "confidence", "operator": "lt", "value": 0.6, "action": "escalate" }
]result | confidence.purchase_intent | action | action_reason |
|---|---|---|---|
route: "sales", purchase_intent: 0.9412 | 0.8824 | continue | { "type": "default" } |
route: "spam", purchase_intent: 0.03 | 0.94 | block | { "type": "policy", "rule": 0 } |
route: "sdr", purchase_intent: 0.45 | 0.1 | escalate | { "type": "policy", "rule": 1 } |
route: "other", purchase_intent: 0.15 | 0.7 | escalate | { "type": "other_option", "question": "route" } |
The confidence of a probability is |2p − 1|, so rule 1 escalates every lead whose purchase_intent falls between 0.2 and 0.8.
In the editor
The Policies card reads each rule as IF condition THEN action:
- Add rule creates a rule on the first question.
- Pick the field — questions, then composites — and what to compare: answer, confidence or, for a score question, weighted level. Then the operator and the value.
- + AND adds a condition, up to 5. Each one has its own field, comparison, operator and value.
- Pick the action after THEN.
Rules are evaluated top to bottom. Renaming or deleting a question updates or removes the rules that start with it; and conditions and composite terms that point at a question that no longer exists are flagged as issues to fix.
Acting on the result
switch (decision.action) {
case "continue":
return routeTo(decision.result.route);
case "block":
return discard(lead);
case "escalate":
return sendToHuman(lead, decision.action_reason);
case "fallback":
return askLargerModel(lead);
}Engine errors: an error or an action — you choose
By default, when the engine times out or fails the API returns an error such as 504 ENGINE_TIMEOUT and no action: decide in your code what an error means — usually the same path as escalate. Set onEngineError to fallback to get a 200 with the fallback action and action_reason.type = "engine_error" instead; those answers aren't billed. See Decision settings and Errors.
Tips
- Put the most specific rules first: the first match wins. Compound (
and) rules usually come before the simple ones they refine. - Use
on: "confidence"withltto escalate answers the engine isn't sure about — or setminConfidenceon the question to use the fallback action instead. Combine both for per-action thresholds. - Use
on: "score"to act between levels —frustration ≥ 3.5— instead of waiting for the top level to win. - Keep
blockfor clear-cut outputs (spam, abuse) and let uncertaintyescalate. - Policies are part of the schema: a change takes effect for API clients only when you deploy a new version.
Composites
Combine several answers into one number with weights you control — Σ(weight × value) / Σ|weight| — return it in composites and use it in policies.
Confidence and probabilities
What confidence means for each question type on one 0–1 scale, how it affects minConfidence, weighted levels, the full probability distributions and near ties.