Skip to main content
The useful thing about an approval is that it is not a refusal. The action does not fail, the agent does not retry, and the budget it asked for is held for it while a person decides. Getting that right is mostly about what your agent does with a 202.

Before you start

Identifiers
The amounts here assume the agent’s starter policy is disabled, as the quickstart does in step 2. Leave it on and it holds anything over $100 for a person before your own rules get a say.

1. Set the threshold

approval_threshold_cents is the shorthand. It compiles to an approval_threshold rule and reports as one:
201 Created
To hold on something other than an amount, write a condition rule with "effect": "require_approval" instead. Passing facts from your own service holds a payment on how new the buyer is, and reaches the same 202.

2. Your agent gets a 202

202 Accepted
There is no cosignature, because nothing is authorized yet. The 24000 is already reserved against the budget, so the next action in the same run cannot quietly spend it while this one waits.

3. Wait, do not retry

A 202 is not a transient failure. Retrying it under a fresh Idempotency-Key creates a second held intent, reserves the budget twice, and asks a person the same question twice.
Reuse the same Idempotency-Key if you must retry the call itself. Kordio replays the original decision rather than holding the spend again.
In production, subscribe to action.requires_approval and approval.resolved and let the webhook wake the run back up. In a script, read the intent back:
200 OK

4. What the approver sees

An approver looking at “approve $240?” is rubber-stamping. One call answers what approving would actually do, and it is the same shape for a payment intent:
200 OK
session is the budget and runtime is the agent, in the wire names the API has always used. Two numbers carry most of the decision: returns_on_deny_cents is what denying gives back, and open_holds counts every other intent competing for the same budget, which is how an approver notices that three pending approvals total more than the budget can cover. The console shows the same facts on the Approvals page, where the queue can be narrowed to live or test agents.

5. Resolve it

200 OK
The intent is now pending, exactly as if it had come back 201, and approval.resolved fires. Your agent executes and reports the outcome with complete or fail, and the reservation is only released when it does. Denying instead moves the intent to denied and returns the reservation:
200 OK
Resolving an intent that is not in requires_approval is a 422, so a double click cannot double execute.

What approvals do not do

Approving is a capability, not a queue with routing. Owners and admins hold approve; members hold read only. Say this out loud to whoever runs the process, because the product does not enforce any of it for you:
  • Kordio does not route a hold to a named person, a group, or an on-call rota. Anyone with approve in the workspace can resolve any hold in it.
  • There is no quorum. One approval resolves the intent, and resolved_by_sub records who.
  • There is no escalation and no expiry. A hold waits until somebody answers it, and its reservation waits with it.
  • An approved intent carries no cosignature. Cosignatures are issued on the authorize call and only for an allowed decision, so an executor that gates on one should read the intent state for this path instead.

Next steps

Roll a policy out safely

Prove the threshold on a test agent before it holds real money.

Webhooks

Wake the run on approval.resolved instead of polling.

Holding spend for a person

The reference for states, capabilities, and the payment path.

Verifying an authorization

Why approval.resolved is not itself permission.