Governance and approvals

Governance is not a permissions layer bolted onto the API — it lives in the domain code both lanes share, so an agent hits exactly the walls a human would. This page covers how those walls surface in responses, and the approval, decision, governance, policy, and related families that operate them.

Holds are results, not errors

When a call crosses a line a human has drawn, the API prefers to succeed and report the hold over failing. The call commits, one audit event lands on the ledger, and the result tells you the action is waiting on a human:

  • A Books signature act without proof returns status: "approval_required" with the exact approval to file — see Books.
  • crm.proposeTerms past a playbook line returns held: true with the reason and a ledgerId.
  • email.send to a first-contact recipient returns status: "approval_required" and queues nothing until released.
  • A refused call-moderation act returns a refusal in the result with one audited *_refused event.
  • Directory structural verbs from an agent file a review item and say so.

Three governance outcomes do surface as errors: a missing scope (403 FORBIDDEN_SCOPE), a lost race (409 CONFLICT), and a gate that blocks the act entirely (423 GOVERNANCE_NOT_READY) — used both for the decision-rights gate below and for signature proof that failed verification.

Report held outcomes honestly

A held result is governance working as designed. Relay it plainly, quoting the ledger reference — "held for signature — ledger #ab12cd34" — and never claim a held or refused act completed. This is the one behavioral rule every client integration must get right.

The approval loop

Approvals bind a human decision to one exact action. The requester can never be the approver — that is a database constraint, not a convention — and resolving needs the approval.decide scope, which agent keys do not hold.

  1. Request — approval.request

    File the classified action with an actionClass, the payload (or a precomputed payloadHash) that binds exactly what is being approved, and an optional human-readable subject. When a call already returned an approval_required frame, file exactly the actionClass, payloadHash, and subject it handed you. Optionally name a board item (itemId) to block on the outcome.

  2. Wait — GET /approval.wait

    Long-poll the outcome: GET /approval.wait?id=appr_…&timeoutMs=55000 returns as soon as the approval resolves, else with its current status at the timeout (cap 55s — loop it).

  3. A human decides

    Pending approvals surface on the workspace home ("Needs you") and the Macro Pulse. A human approves or rejects; for signature acts, approving mints the cryptographic signature token bound to that exact act.

  4. Retry with proof

    Re-issue the original call with approvalId (or the minted signatureToken) in its params and a fresh x-idempotency-key. The proof is single-use and bound to the org, action class, subject row, and payload hash — approve-then-swap fails closed.

The Needs-you surface with a pending approval awaiting a human decision
Where a filed approval lands — the human decides here

approval.get and approval.list read your own approvals back; approval.resolve is the deciding verb for principals that hold approval.decide.

The decision-rights gate

A handful of methods let agents redistribute work and adopt decisions: board.decompose, board.assign, all four handoff.* verbs, decision.adopt, and decision.supersede. These refuse with 423 GOVERNANCE_NOT_READY until a workspace admin has declared who holds which decision rights (governance.set_decision_rights) — an autonomous fleet must not self-assign work before a human has said who may decide what. Check readiness with governance.status.

Decisions, policy, and the reserved namespaces

  • Decisions registrydecision.propose, comment, sign, reverse, requestAdjustment, listComments, plus the gated adopt / supersede; read the adopted set with GET /decision.list. Signed decisions are the workspace's durable "we chose X because Y".
  • Policy distribution — admins publish signed policy bundles (policy.publish); every key can fetch the current set with GET /policy (filter with ?kind=).
  • Credential broker — org-level third-party credentials are held once, server-side: admins credential.put / list / revoke; agents credential.lease a short-lived, scoped credential instead of ever holding the org key. Lease results are never cached (see Idempotency).
  • Kill switchadmin.deactivate / admin.reactivate halt an agent fleet-wide; a halted agent's presence beats return a halt directive.
  • Pairingpairing.request (pre-auth, org-pinned, heavily throttled) then pairing.approve (admin) is how a new agent obtains its key.

The namespaces admin, pairing, policy, and governance are reserved: their methods always require the admin scope, regardless of registry entries — defence in depth against a descriptor typo widening access.

Audit is part of the semantics

Every governance outcome — the hold, the approval, the decision, the refusal — lands on the hash-chained ledger with the acting principal, so "who tried what, who allowed it, and when" is one verifiable history. See The event ledger.