A runtime governance model for autonomous AI agents calling production APIs.
Existing access controls answer questions about the caller. OAuth scopes describe which operations a client may invoke. RBAC describes what role it holds. Rate limits describe how often it may call. None of them answers the question that matters once the caller is an autonomous agent: what happens if this particular call is wrong?
An agent can be fully authenticated, fully authorised, within its rate limit, and still initiate a payment that should never have been initiated. Identity is not consequence.
This repository holds a specification and a worked reference for closing that gap: classify each operation by the consequence of getting it wrong, declare the classification in the API specification, and enforce it at the gateway.
Three layers, not two.
| Layer | Question | Mechanism | Describes |
|---|---|---|---|
| Identity | Who is calling? | mTLS, tokens, agent ID, delegation chain | the caller |
| Authorisation | What are they permitted to do? | OAuth scopes, RBAC, scope reduction | the caller |
| Consequence | What happens if this call is wrong? | consequence tier, enforced at the gateway | the action |
Five criteria classify any operation.
- Reversibility — does the undo restore the effect, restore only the record, or not exist?
- Blast radius — one record, one customer, a population, or system-wide?
- Data sensitivity — public, internal, personal, or regulated?
- Compliance impact — none, adjacent, or a direct regulatory trigger?
- Idempotency — safe to run twice, safe only with a key, or unsafe?
Reversibility is three states rather than two, and the middle state is where most consequential operations sit. You can put a price back; the orders placed at the wrong price were still placed. You can revoke an export; the data has left. The reversal stops the bleeding without restoring the state.
Four tiers follow.
| Tier | Character | Control | Human role |
|---|---|---|---|
| 1 — Low | Read-only, no compliance surface | Log only; logging does not gate | None |
| 2 — Medium | Undo restores the effect | Audit trail, before/after state, verified compensating action | Decides whether it stands |
| 3 — High | Undo restores the record, not the effect | Gateway withholds; async approval; approver recorded | Out-of-band approval |
| 4 — Critical | No undo exists | Agent suspended before the action; no auto-resume | Performs it personally |
Tier 3 is a delay. Tier 4 is a handover. In Tier 3 the operation is deferred and the policy service eventually executes it under its own identity, with the originating agent and the authorising person recorded as attribution. In Tier 4 the operation is withdrawn — the agent never executes it, and the human performs the action themselves under their own credentials.
The human is never in the API request path.
This is the part that makes the model deployable rather than theoretical. A human placed inside the request path holds an HTTP connection open for minutes while somebody reads a message queue. Gateway timeouts fire, connection pools drain, and — worst — the agent's own retry logic fires against the pending request, so a control built to prevent an operation causes it to happen twice.
Instead, for Tier 3 the gateway:
- Withholds the operation. Nothing is routed.
- Persists the request (method, target, query, body), the delegation chain, an idempotency key and an expiry — not the agent's credential.
- Emits an asynchronous authorisation event to an orchestration layer.
- Returns a terminal, deliberately non-retryable response to the agent, which is then free to continue with the rest of its task.
- Executes what was approved — method, request target, query and body byte-identical to what the reviewer saw. Persist-and-execute is how the reference meets C6; the agent is not the HTTP client.
The human decides on human timescales. No connection, worker, or client timeout is consumed.
Tiers are evaluated at design time by the interface owner and recorded in the OpenAPI document. At runtime the gateway resolves the recorded tier — a lookup, not an evaluation, which is why it fits inside a production latency budget — and then applies what the specification could not know:
- accumulated consequence across the session, task or delegation chain
- delegation chain integrity, where a child agent holds broader authority than its parent
- request content exceeding the bounds the declaration assumed
Escalation is one-directional. Runtime information can raise a tier and can never lower it. The declared tier is a floor, not a determination — which also means under-declaring an endpoint buys nobody anything.
paths:
/accounts/{accountId}/balance:
get:
operationId: getBalance
x-ctier-tier: 1
/payments:
post:
operationId: initiatePayment
x-ctier-tier: 4
/customers/{customerId}/profile:
put:
operationId: updateProfile
# no tier declared -> the gateway treats this as Tier 4No tier declared = Tier 4. Unclassified operations fail closed, so coverage gaps surface instead of hiding.
If an agent has no business anywhere near an operation, do not classify it — do not grant the scope. That is exclusion, and it happens at the authorisation layer, before any tier is considered.
Tier 4 is for operations the agent genuinely participates in but must not complete. Both are
needed, because scopes are always coarser than operations: accounts:write covers a Tier 2
preference update and a Tier 4 regulatory determination, and you cannot withhold one without
losing the other.
Exclude at the scope; tier within it.
| Path | What it is |
|---|---|
spec/consequence-tiered-api.yaml |
Complete worked OpenAPI 3.1 example — operations across all four tiers, plus one explicitly excluded and one deliberately unclassified |
spec/README.md |
Specification 1.25.0 index and naming note |
spec/conformance.md |
What the corpus establishes and what it does not |
spec/headers.md |
HTTP headers at the trust boundary |
spec/schemas/ |
JSON Schema 2020-12 for the document contract |
docs/ |
Classification worksheet, domain walkthroughs, FAQ |
docs/adr/0009-the-contract-is-the-documents.md |
Why the contract is documents, not a library API |
The example specification is deliberately built so the argument is visible in its structure:
the OAuth scopes are drawn at the granularity real systems use, so the mismatch between scope
and consequence shows on the page. accounts:read reaches both a Tier 1 balance query and an
unclassified bulk export. payments:write would reach a small refund and a large transfer
identically.
Two operations in it are worth reading first:
updateFeatureFlagsis Tier 4 withdata-sensitivity: internal. No personal data anywhere in the operation, and it is still critical — blast radius and irrecoverability carry it. Classification by data sensitivity alone misses the most dangerous operations on a surface.listTransactionsis Tier 1 individually and carries an accumulation rule that escalates it on repetition. A sequence of individually low-consequence reads that reconstructs a complete record is the obvious attack on any per-call control, and it has to be handled at session level or the whole model is decorative.
Five steps, roughly a week, no new platform required.
- Inventory what your agents can actually call. Pull the scopes on agent service accounts and enumerate the operations those scopes reach. The list is usually longer than expected.
- Classify the top twenty by hand. Enough to reveal the shape of your risk, small enough to finish in an afternoon. Resist building tooling first.
- Find the ones that are wrong — operations you assumed were safe that turn out to be irreversible, and operations you have been gating that are provably Tier 1. The second group is where you win credibility, because you are giving time back.
- Declare tiers in the spec. Costs nothing before you enforce anything, and it forces the conversation with interface owners while it is still cheap.
- Enforce one tier, at one gateway, for one agent. You will learn more from one working async approval path than from a taxonomy covering everything and enforcing nothing.
What not to do first: build a classification service, write a policy document, or try to classify every operation before enforcing any of them. All three feel like progress and none produce a governed operation.
Specification 1.25.0 and reference example. Reference implementation: open-gw/ctier-engine.
Naming. 1.0.0 — the archived specification at
doi.org/10.5281/zenodo.22020288 —
used x-consequence-*. 1.1.0 renames those extensions to the ctier
namespace (x-ctier-tier, x-ctier-criteria, x-ctier-exclude,
x-ctier-defaults, and the other flat keys in spec/). The 1.0.0 archive is
unaltered and remains resolvable at that DOI.
Subject matter described here is the subject of US provisional patent application 64/137,066
(filed 19 August 2026). The specification and documentation are licensed CC BY 4.0; that licence
covers copyright and grants no patent licence. See LICENSE.
Tier boundaries are intended to be configured per organisation — if your refund threshold differs from the one in the example, the model is working as designed. Issues and pull requests proposing different boundaries, additional criteria, or gaps in the accumulation logic are welcome and are the reason this is public.
See CITATION.cff.
Specification and documentation: CC BY 4.0. See LICENSE.