Skip to content
Warrantv0.1

Escalations

What happens when a check does not cleanly pass.

An escalation is a warrant handed to a person, with the funds still held and the deadline still running.

{
  "id": "esc_01J8Z3M4YQ7K2V9X",
  "warrant_id": "wrt_01J8Z3M4YQ7K2V9X",
  "amount": "8400000",
  "asset": "USDC",
  "payee": "api.exampledata.io",
  "reason_codes": ["data_stale"],
  "score": 6667,
  "opened_at": "2026-08-15T12:00:00.000Z",
  "resolved_at": null,
  "resolution": null
}

Escalating is a decision, not a failure to decide

A policy resolves to escalate when the score lands in a band it will not settle automatically, or when a reason code appears in alwaysEscalate. Both are the policy working, not the policy giving up.

The money stays held throughout. Nothing about an open escalation is ambiguous from the ledger's point of view: the funds are in held, they are not in payee, and they are not back in available.

The four resolutions

resolutionWhat happens
releasedThe payee is paid in full.
partially_releasedA share is paid, the remainder returns to available.
refundedNothing is paid; the whole amount returns to available.
expiredThe deadline passed with nobody deciding.

The API accepts release, partial_release and refund. expired is not something a person can choose — it is what the sweep records when the clock runs out.

released_bps is required on a partial release and must be strictly between 0 and 10000. Zero is a refund and 10000 is a release, so neither needs a third name.

Nothing waits forever

POST /internal/jobs/expire sweeps overdue warrants. An escalation nobody resolves does not sit indefinitely: the warrant goes held → expired → refunded and the money is back in available before the sweep returns.

This is the invariant the whole product rests on. Every path out of an escalation ends with the funds somewhere definite, and no path leaves them held.

Above the threshold, a person signs

An organisation can set passkey_threshold. A settlement at or above it does not complete on the first request: the API answers with a WebAuthn challenge, and the decision only lands when the browser returns an assertion.

{
  "passkey_required": true,
  "challenge": "…",
  "scope": "resolve"
}

Resubmit with assertion carrying challenge, credential_id and signature.

An API key can never do this. Keys carry no role and cannot resolve an escalation at all — if they could, the threshold would be decorative, since anyone holding a key could route around it.

The challenge is consumed on use, so a captured assertion cannot be replayed, and a passkey whose signature counter fails to advance is rejected as a probable clone.

What a notification carries

An escalation notifies everyone who can act on it — operators and above, since a viewer cannot resolve one and paging them is noise. The notification carries four facts, and they are chosen so the decision can be made from a lock screen: amount, payee, which clause failed, and how long is left.

Approving still requires opening the console and using a passkey. A Telegram message or a push notification can carry the escalation; neither can release funds.

Amounts

Integer strings of the asset's smallest unit, matching ^[0-9]+$.

"8400000"    8.40 USDC
8400000      rejected — a JSON number
"8.40"       rejected — not the smallest unit

score is basis points, not a fraction: 6667 means two clauses of three passed. It shares the integer discipline of amounts for the same reason — a threshold comparison against a float is a threshold you cannot reason about.