Policies
The rules that decide release, escalation or refund.
A policy is an ordered list of score bands plus a set of reason-code overrides, and it is the only thing that decides what happens to a warrant once the verifier reports.
{
"bands": [
{ "minScore": 1, "outcome": "release", "label": "clean pass" },
{ "minScore": 0.8, "outcome": "partial_release", "releasedBps": 5000 },
{ "minScore": 0, "outcome": "escalate", "label": "anything else" }
],
"maxAmount": "50000000",
"alwaysEscalate": ["source_unreachable"],
"alwaysRefund": ["source_not_found"]
}The four outcomes
A policy resolves to exactly one of release, partial_release, escalate or
refund. There is no fifth answer and no way to decline to answer, because a
policy that could abstain would leave funds held forever.
Bands are read from the top score down
Bands are sorted by minScore descending, and the first one the score reaches
wins. The score comes from the verifier: the share of
clauses that passed, stored as basis points so it is exact rather than a float
you have to trust.
A partial_release band must carry releasedBps strictly between 0 and 10000.
A band that pays nothing is a refund and a band that pays everything is a
release, so neither needs to be spelled as a partial.
Validation happens when the policy is written, not when a warrant settles. A
policy with no band at minScore: 0 is rejected with policy_no_floor_band,
because a low score would otherwise match nothing and the money would sit.
Reason codes override the score
alwaysRefund is checked first, then alwaysEscalate, and only then the bands.
This ordering is deliberate. "The source did not exist" is not a quality judgement, and a band should not be allowed to pay out 50% on it just because two of four clauses happened to pass. A reason code describes what went wrong; a score describes how much went right, and they are not the same question.
Amounts are integer strings of the smallest unit
maxAmount follows the same convention as every amount on the wire: a string of
digits in the asset's smallest unit, validated against ^[0-9]+$.
"8400000" 8.40 USDC
8400000 rejected — a JSON number
"8.40" rejected — not the smallest unitThe string is not decoration. 9007199254740993 as a JSON number silently loses
precision as a double, and this is a payments API. The decimal point is a
display concern and lives in the console, never on the wire.
Which policy applies
Policies are per agent, resolved newest-version-first:
- the agent's own newest policy version, if it has one
- otherwise the organisation default — the policy with a null
agentId - otherwise the built-in default
The built-in default is the strictest sensible rule and pays only on a clean pass:
{
"bands": [
{ "minScore": 1, "outcome": "release", "label": "clean pass" },
{ "minScore": 0, "outcome": "escalate", "label": "anything else" }
]
}Versions, never edits
A policy row is never edited in place. Writing new rules creates a new version
under the same name, and a warrant pins the version it ran under.
The rules_hash — a SHA-256 of the rules object — is recorded on the
trace. So a settlement from March can be re-read against the
exact rules that produced it, and the record proves which those were rather than
asking you to take the current row's word for it.