What your agent sees
When a rule holds an action, the authorize call returns202 and the intent is in state
requires_approval:
202 Accepted
cosignature. Nothing has been authorized yet, and nothing mints one later either:
approving moves the intent to pending and returns it, without a signature.
The budget is reserved while the intent waits, so a held action cannot be quietly outspent by
the next one. headroom was measured before that reservation, which is why it reads $4,800
rather than what is left now.
How your agent should wait
Do not busy-poll, and do not treat a hold as a failure to retry around. Retrying with a new idempotency key creates a second held intent and asks a person the same question twice.Webhook
Subscribe to
action.requires_approval and approval.resolved. This is the one to use in
production.Poll the intent
GET /control/v1/agent/actions/{id} until state leaves requires_approval. Fine for a script.What the person sees
An approver who only sees “approve $2,400?” is rubber-stamping. So there is an endpoint that answers what approving would actually do, before anyone clicks:200 OK
session is the budget and runtime is the agent, in the wire names this endpoint has always
used. Three figures carry the decision. Budget is already reserved while an intent is held,
so approving costs no further headroom. returns_on_deny_cents is what denying gives back. And
open_holds counts the held intents competing for the same budget, including this one, which is
how an approver sees that three pending approvals total more than the budget can cover.
executed is always false here. The endpoint answers what approving would do, and Kordio never
executes anything, so it has nothing else to report.
GET .../payment_intents/{id}/impact is the same shape, plus the spend token the payment cites,
if any.
Resolving it
approve capability, and both record resolved_by_sub, which is who
decided, and emit approval.resolved.
Actions and payments resolve the same way
Approving grants authority. It never moves money: an approved payment is still waiting on your
agent to execute it and say what happened.
Resolving an intent that is not in
requires_approval is a 422, so a double-click cannot
double-execute.
Who can approve
Approving is a capability, not a job title.approve is separate from manage_policy, so the
person who authorises a payment need not be the person who can widen the rule that held it.
Keep those apart. Segregation of duties is most of what an auditor is looking for here.
What the product does not do for you: it will not route a hold to a named person, a rota, or a
group, and it has no quorum. Anyone holding approve in the workspace can resolve any hold in
it, one call resolves it, and resolved_by_sub records who. There is also no escalation and no
expiry, so a hold that nobody answers waits, and its reservation waits with it.
Next steps
Webhooks
Wait on
approval.resolved instead of polling.Verifying an authorization
Why
approval.resolved is not itself permission.