AAuth Budgets
draft-hardt-aauth-budgets-00
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is not endorsed by the IETF and has no formal standing in the
IETF standards process.
| Document | Type | Active Internet-Draft (individual) | |
|---|---|---|---|
| Author | Dick Hardt | ||
| Last updated | 2026-09-28 | ||
| RFC stream | (None) | ||
| Intended RFC status | (None) | ||
| Formats | |||
| Stream | Stream state | (No stream defined) | |
| Consensus boilerplate | Unknown | ||
| RFC Editor Note | (None) | ||
| IESG | IESG state | I-D Exists | |
| Telechat date | (None) | ||
| Responsible AD | (None) | ||
| Send notices to | (None) |
draft-hardt-aauth-budgets-00
TBD D. Hardt
Internet-Draft Hellō
Intended status: Standards Track 28 September 2026
Expires: 1 April 2027
AAuth Budgets
draft-hardt-aauth-budgets-00
Abstract
This document defines AAuth Budgets, an extension to the AAuth
Protocol ([I-D.hardt-oauth-aauth-protocol]) that carries a spending
ceiling from a person server to a resource. A budget is a ceiling on
what an agent may consume at one resource, denominated in a unit the
resource declares, carried as a claim in the auth token, and enforced
by the resource. Budgets are structurally parallel to scope: the
agent asks, the resource offers, the person server and access server
may narrow, and the auth token carries what was granted. The
extension adds a budget claim to resource tokens and auth tokens, a
budget_consumed claim reporting what the presented auth token
consumed, a budget_units field and a usage_endpoint to resource
metadata, and an AAuth-Budget response header reporting what a
request cost and what remains.
Discussion Venues
_Note: This section is to be removed before publishing as an RFC._
This document is part of the AAuth specification family. Source for
this draft and an issue tracker can be found at
https://github.com/dickhardt/AAuth (https://github.com/dickhardt/
AAuth).
Status of This Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
Hardt Expires 1 April 2027 [Page 1]
Internet-Draft AAuth-Budgets September 2026
This Internet-Draft will expire on 1 April 2027.
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the
document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents (https://trustee.ietf.org/
license-info) in effect on the date of publication of this document.
Please review these documents carefully, as they describe your rights
and restrictions with respect to this document. Code Components
extracted from this document must include Revised BSD License text as
described in Section 4.e of the Trust Legal Provisions and are
provided without warranty as described in the Revised BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1. Non-Goals . . . . . . . . . . . . . . . . . . . . . . . . 5
2. Conventions and Definitions . . . . . . . . . . . . . . . . . 6
3. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 6
4. Budget Model . . . . . . . . . . . . . . . . . . . . . . . . 6
4.1. A Budget Is Structurally a Scope . . . . . . . . . . . . 6
4.2. The Narrowing Chain . . . . . . . . . . . . . . . . . . . 7
4.3. Value Representation . . . . . . . . . . . . . . . . . . 9
4.4. Range Limits . . . . . . . . . . . . . . . . . . . . . . 9
4.5. The Budget Object . . . . . . . . . . . . . . . . . . . . 10
5. Resource Metadata Extensions . . . . . . . . . . . . . . . . 10
6. Authorization Endpoint Extensions . . . . . . . . . . . . . . 12
7. Resource Token Extensions . . . . . . . . . . . . . . . . . . 13
7.1. The Consumption Record . . . . . . . . . . . . . . . . . 13
8. Auth Token Endpoint Extensions . . . . . . . . . . . . . . . 15
8.1. What the PS Is Deciding . . . . . . . . . . . . . . . . . 15
8.2. What the PS Reads . . . . . . . . . . . . . . . . . . . . 16
8.3. How the PS Responds . . . . . . . . . . . . . . . . . . . 16
9. PS-to-AS Token Request Extensions . . . . . . . . . . . . . . 18
10. Auth Token Extensions . . . . . . . . . . . . . . . . . . . . 20
11. AAuth-Budget Response Header . . . . . . . . . . . . . . . . 21
11.1. Sending Rules . . . . . . . . . . . . . . . . . . . . . 22
11.2. Denomination Conflict . . . . . . . . . . . . . . . . . 23
12. Streaming . . . . . . . . . . . . . . . . . . . . . . . . . . 23
12.1. When cost Is Omitted . . . . . . . . . . . . . . . . . . 23
12.2. Trailer Rules . . . . . . . . . . . . . . . . . . . . . 24
12.3. Ambiguous Failures . . . . . . . . . . . . . . . . . . . 25
13. Budget Exhaustion . . . . . . . . . . . . . . . . . . . . . . 25
13.1. The reason Parameter . . . . . . . . . . . . . . . . . . 26
13.2. Refusing a Request That Does Not Fit . . . . . . . . . . 27
Hardt Expires 1 April 2027 [Page 2]
Internet-Draft AAuth-Budgets September 2026
13.3. Deferred Delivery . . . . . . . . . . . . . . . . . . . 28
13.4. Boundaries . . . . . . . . . . . . . . . . . . . . . . . 29
14. Enforcement . . . . . . . . . . . . . . . . . . . . . . . . . 30
14.1. The Resource Enforces . . . . . . . . . . . . . . . . . 30
14.2. Budgets Require Auth-Token Mode . . . . . . . . . . . . 30
14.3. Token Scope . . . . . . . . . . . . . . . . . . . . . . 30
14.4. Unreported Allocations . . . . . . . . . . . . . . . . . 31
14.4.1. Settlement . . . . . . . . . . . . . . . . . . . . . 32
14.5. Aggregation . . . . . . . . . . . . . . . . . . . . . . 33
14.6. The Billing Account . . . . . . . . . . . . . . . . . . 34
14.7. Concurrency . . . . . . . . . . . . . . . . . . . . . . 36
14.8. The Budget Is a Hard Cap . . . . . . . . . . . . . . . . 36
14.9. Failed Calls . . . . . . . . . . . . . . . . . . . . . . 37
14.10. Delegation . . . . . . . . . . . . . . . . . . . . . . . 37
14.11. Mission Attribution . . . . . . . . . . . . . . . . . . 37
15. Usage Counters . . . . . . . . . . . . . . . . . . . . . . . 38
15.1. Usage Request . . . . . . . . . . . . . . . . . . . . . 38
15.2. Usage Response . . . . . . . . . . . . . . . . . . . . . 39
15.2.1. Calendar Counters . . . . . . . . . . . . . . . . . 40
15.2.2. Per-Key Figures . . . . . . . . . . . . . . . . . . 41
15.2.3. One Unit Per Response . . . . . . . . . . . . . . . 41
15.2.4. The Signed Response . . . . . . . . . . . . . . . . 42
15.3. Authorization and Errors . . . . . . . . . . . . . . . . 43
15.4. Division of Labor . . . . . . . . . . . . . . . . . . . 43
16. Capability Negotiation . . . . . . . . . . . . . . . . . . . 44
17. Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
17.1. Declining with Guidance . . . . . . . . . . . . . . . . 46
18. Standing Authorization for Metered Inference . . . . . . . . 46
19. Security Considerations . . . . . . . . . . . . . . . . . . . 47
19.1. The Header Is Unsigned . . . . . . . . . . . . . . . . . 47
19.2. Budget Is Not a Substitute for Scope . . . . . . . . . . 48
19.3. Over-Issuance Through Concurrency . . . . . . . . . . . 48
19.4. Consumption Reports as Attack Surface . . . . . . . . . 48
19.5. Unit Substitution . . . . . . . . . . . . . . . . . . . 49
20. Privacy Considerations . . . . . . . . . . . . . . . . . . . 49
21. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 49
21.1. HTTP Header Field Registration . . . . . . . . . . . . . 50
21.2. JWT Claims Registration . . . . . . . . . . . . . . . . 50
21.3. AAuth Capability Value Registry . . . . . . . . . . . . 50
21.4. No Budget Unit Registry . . . . . . . . . . . . . . . . 51
22. Implementation Status . . . . . . . . . . . . . . . . . . . . 51
22.1. Implementations in Progress . . . . . . . . . . . . . . 51
23. Document History . . . . . . . . . . . . . . . . . . . . . . 51
24. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 52
25. References . . . . . . . . . . . . . . . . . . . . . . . . . 52
25.1. Normative References . . . . . . . . . . . . . . . . . . 52
25.2. Informative References . . . . . . . . . . . . . . . . . 53
Appendix A. Design Rationale . . . . . . . . . . . . . . . . . . 54
Hardt Expires 1 April 2027 [Page 3]
Internet-Draft AAuth-Budgets September 2026
A.1. Why an Integer in a Declared Scale . . . . . . . . . . . 54
A.2. Why the Scale Is Explicit . . . . . . . . . . . . . . . . 54
A.3. Why decimals Is in the Signed Claim . . . . . . . . . . . 55
A.4. Why One Record . . . . . . . . . . . . . . . . . . . . . 55
A.5. Why the Agent Is Not Told Its Cumulative Consumption . . 56
A.6. Why the Granted Budget Is Not the Person's Ceiling . . . 57
A.7. Why a Trailer Only Adds a Member . . . . . . . . . . . . 58
A.8. Why Not RateLimit . . . . . . . . . . . . . . . . . . . . 58
A.9. Why Budget Is Not a Mission Aggregate . . . . . . . . . . 59
A.10. Why Units Are Not Registered . . . . . . . . . . . . . . 59
Appendix B. Prior Art . . . . . . . . . . . . . . . . . . . . . 60
B.1. RFC 9396, Rich Authorization Requests . . . . . . . . . . 60
B.2. TPX . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
B.3. UK Open Banking Variable Recurring Payments . . . . . . . 61
B.4. Stripe Issuing Spending Controls . . . . . . . . . . . . 61
B.5. W3C Payment Request API . . . . . . . . . . . . . . . . . 62
B.6. x402 . . . . . . . . . . . . . . . . . . . . . . . . . . 62
B.7. ODRL . . . . . . . . . . . . . . . . . . . . . . . . . . 62
B.8. RateLimit Header Fields . . . . . . . . . . . . . . . . . 62
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 62
1. Introduction
*Status: Exploratory Draft*
The AAuth Protocol ([I-D.hardt-oauth-aauth-protocol]) lets a person
server (PS) decide whether an agent may access a resource, and lets
the resource express what access it offers. Neither party has a way
to say _how much_.
For a resource that meters and charges per call, that omission is the
whole authorization decision. An agent harness calling a model
inference endpoint on a person's account can spend without bound: the
scope inference.completions is either granted or not, and once
granted it says nothing about whether the agent may consume ten cents
or ten thousand dollars of the person's money. The person's only
controls are outside the protocol — a provider dashboard, a card
limit, a bill that arrives after the fact.
This document defines a *budget*: a ceiling on what an agent may
consume at one resource, denominated in a unit the resource declares,
carried as a claim in the auth token, and enforced by the resource.
Hardt Expires 1 April 2027 [Page 4]
Internet-Draft AAuth-Budgets September 2026
A budget is an authorization, not a hint. The PS has authorized the
agent to spend up to a stated amount, and the resource is the party
that counts. That distinction determines nearly every design choice
in this document, in particular why the balance cannot be reported
through RateLimit ([I-D.ietf-httpapi-ratelimit-headers]) — see
Appendix A.8.
The granted budget is an allocation, not the person's ceiling. The
ceiling is the person server's own state: no claim carries it, and it
may not be shared with the agent. The PS sizes each auth token
against what the work has cost so far, and the token's expiry or its
budget's exhaustion — whichever comes first — brings the agent back
for the next allocation. That return is the supervision point, and
the PS's options there are the subject of Section 8. Nobody knows at
mission approval what an agent's work will cost; a figure fixed once
up front is either too small to finish or too large to be a control
Appendix A.6.
Metered inference is the initiating use case, and Section 18 covers
it as a named deployment pattern. The mechanism is general: any
resource that meters and charges per call uses it unchanged.
TPX [TPX] profiles the same grant for OAuth 2.0: a person grants a
human-driven app a metered inference budget — "a damage cap, not a
payment" — from a provider the person chooses and pays. This
document is the AAuth counterpart: the same grant, carried to an
autonomous agent through the narrowing chain, and generalized beyond
inference to any resource that meters. A provider implementing both
accepts two authorization envelopes over one meter Section 18.
1.1. Non-Goals
* *Not a mission aggregate.* A budget covers a single resource in a
single unit. It does not express a cross-resource total such as
"$5,000 for the Japan trip." Mission-wide totals require
aggregation across resources that meter in different units, which
this document does not define.
* *Not a rate limit.* A budget is cumulative consumption, not per-
window throughput. RateLimit and RateLimit-Policy
([I-D.ietf-httpapi-ratelimit-headers]) cover throughput. Both MAY
appear on the same response as AAuth-Budget, meaning different
things.
* *Not pricing.* The resource prices its own service. A budget
bounds spend at whatever prices the resource charges; this
document defines no way to express a price.
Hardt Expires 1 April 2027 [Page 5]
Internet-Draft AAuth-Budgets September 2026
* *Not composite.* A budget is one amount in one unit. A resource
that meters several quantities at different rates — input tokens,
output tokens, cache reads — collapses them to one billing unit,
typically currency, before denominating a budget.
* *Not payment or settlement.* No funds move. 402 Payment Required
and the resource's commercial arrangement with the person are
untouched.
* *Not PS-enforced at request time.* The PS authorizes a number.
The resource counts against it. The PS is not in the request
path.
* *Not an OAuth extension.* This document defines claims in AAuth
tokens (aa-resource+jwt, aa-auth+jwt), fields in aauth-
resource.json, a resource endpoint, an AAuth capability value, and
an AAuth response header. It registers nothing in an OAuth
registry. The documents surveyed in Appendix B are cited as prior
art and are non-normative.
2. Conventions and Definitions
{::boilerplate bcp14-tagged}
3. Terminology
* *Budget*: A ceiling on what an agent may consume at one resource,
expressed as an amount in a unit the resource declares.
* *Unit*: A resource-declared identifier for what is being metered —
a currency code, a token count, a call count.
* *Scale*: The number of decimal places implied by a budget amount,
carried as decimals. An amount of 5000000 with decimals of 6 is
5.000000 of the unit.
* *Granted budget*: The budget claim of an auth token. The figure
the resource enforces against.
* *Consumption*: What the resource has metered against a granted
budget, in the same unit and scale.
* *Consumption record*: A {jti, consumed} pair reporting what the
resource metered against one auth token's budget, as of the
resource token that carries it. Carried in the budget_consumed
claim of a resource token.
* *Usage counters*: Calendar-aligned consumption totals a person
server reads at the resource's usage_endpoint.
4. Budget Model
4.1. A Budget Is Structurally a Scope
The AAuth Protocol defines scope in three positions with a narrowing
rule ([I-D.hardt-oauth-aauth-protocol], Scopes). A budget occupies
the same three positions, plus the PS-to-AS hop in four-party access:
Hardt Expires 1 April 2027 [Page 6]
Internet-Draft AAuth-Budgets September 2026
+==================+===================+===================+
| Position | scope | budget |
+==================+===================+===================+
| Authorization | what the agent | what the agent |
| endpoint request | asks for | asks for |
+------------------+-------------------+-------------------+
| Resource token | what the resource | what the resource |
| | will grant | will grant |
+------------------+-------------------+-------------------+
| PS-to-AS token | (n/a) | what the PS will |
| request | | allow |
+------------------+-------------------+-------------------+
| Auth token | granted, MUST NOT | granted, MUST NOT |
| | be broader | exceed |
+------------------+-------------------+-------------------+
Table 1
The base protocol's rule that a resource token MUST only include
resource scopes the resource has declared in its scope_descriptions
metadata has a direct parallel here: a resource token MUST only name
a unit the resource has declared in budget_units Section 5.
This extension therefore introduces one new claim shape and no new
authorization semantics. Every party that already knows how to
narrow a scope knows how to narrow a budget.
4.2. The Narrowing Chain
Each stage MUST NOT exceed the previous stage. There is one
asymmetry between the amount and the denomination: the resource
settles the denomination, and no later party may change it.
Hardt Expires 1 April 2027 [Page 7]
Internet-Draft AAuth-Budgets September 2026
Agent Resource PS AS
| | | |
| budget request | | |
| (OPTIONAL) | | |
|---------------->| | |
| | sets unit and decimals, |
| | MAY lower amount |
| | | |
| resource token (budget) | |
|<----------------| | |
| | | |
| resource token | | |
|-------------------------------->| |
| | | MAY lower |
| | | amount |
| | | |
| | budget (four-party only) |
| | |--------------->|
| | | | MAY lower
| | | | amount
| | | auth token |
| | |<---------------|
| auth token (granted budget) | |
|<--------------------------------| |
{: #fig-narrowing title="Budget narrowing. Only the resource sets
the unit and scale."}
1. *The agent requests.* OPTIONAL. A budget object in the
authorization endpoint request Section 6. Omitting it means the
resource applies its own default. Requesting more than the
resource will allow is NOT an error; the resource narrows.
2. *The resource sets the unit and scale, and MAY lower the amount.*
The resource is the enforcer and the only party that knows its
own pricing, so it settles the denomination. It MAY change unit
from what the agent requested — for example converting a request
denominated in tokens into a currency amount. After this stage,
unit and decimals are fixed for the life of the grant.
3. *The PS MAY lower the amount.* The PS MUST NOT change unit or
decimals. It is applying the person's policy to a figure the
resource denominated; a PS that redenominated would be stating a
budget in something the resource may not meter in.
4. *The AS MAY lower the amount* (four-party only), for its own
credit or risk reasons. The same prohibition on changing unit or
decimals applies.
Hardt Expires 1 April 2027 [Page 8]
Internet-Draft AAuth-Budgets September 2026
Because the resource MAY change the unit, an agent MUST NOT assume
the granted budget is directly comparable to what it requested. The
agent reads what it actually got from the budget claim of its auth
token.
The resource token carries one figure, not both the agent's request
and the resource's own maximum. It is the minimum of the two,
exactly as scope is. What the agent originally asked for has no
bearing on the PS's decision; an agent that wants the PS to know it
belongs in justification ([I-D.hardt-oauth-aauth-protocol], Auth
Token Endpoint).
4.3. Value Representation
A budget amount is a *non-negative integer in a scale the resource
declares*. It is not a floating-point number and not a decimal
string.
The value of a budget is amount divided by 10 raised to the power of
decimals, in unit. An amount of 5000000 with unit of USD and
decimals of 6 is five US dollars.
The scale is declared per unit, not globally. A resource metering US
dollars per inference call wants decimals of 6 to represent micro-
dollars; a resource metering Japanese yen wants 0, matching the ISO
4217 [ISO4217] minor unit; a resource metering tokens wants 0.
decimals is the name x402 [x402] and ERC-20 [ERC20] use for this
quantity. ISO 4217 and ISO 20022 call it the minor unit. This
document does not introduce a third name. See Appendix A.1 for why
the value is an integer and Appendix A.2 for why the scale travels
with the amount rather than being derived from the unit identifier.
4.4. Range Limits
Two carriers bound the representable range:
* A Structured Field Integer ([RFC9651], Section 3.3.1) is limited
to 15 digits, which bounds remaining, cost, and reserved in the
AAuth-Budget header Section 11.
* A JSON number is exact only to 2^53 (approximately 9.0 x 10^15),
which bounds the amount member of the budget claim.
US dollars at decimals of 6 therefore top out near 10^9 per budget,
which is well beyond any plausible grant. Implementations MUST NOT
issue a budget whose amount exceeds 999,999,999,999,999 (15 digits),
so that the amount and every derived figure remain representable in
both carriers.
Hardt Expires 1 April 2027 [Page 9]
Internet-Draft AAuth-Budgets September 2026
If assets requiring 18 decimal places come into scope, both carriers
break and amount would have to become a string, as it is in x402.
This document does not define that change.
4.5. The Budget Object
One shape appears in every position — the authorization endpoint
request, the resource token, the PS-to-AS token request, and the auth
token:
{
"amount": 5000000,
"unit": "USD",
"decimals": 6
}
Members:
* *amount* (REQUIRED). A non-negative integer, subject to
Section 4.4.
* *unit* (REQUIRED). A string identifying what is metered. For
currency, the value SHOULD be an ISO 4217 [ISO4217] alphabetic
code. Units are resource-declared; this document establishes no
registry of unit values, for the same reason the base protocol
establishes no registry of scope values.
* *decimals* (REQUIRED). A non-negative integer giving the scale of
amount. When unit is an ISO 4217 code, decimals is NOT
constrained to that currency's minor unit; see Appendix A.2.
The object appears alongside scope wherever both are present:
{
"scope": "inference.completions",
"budget": { "amount": 5000000, "unit": "USD", "decimals": 6 }
}
5. Resource Metadata Extensions
This document extends the /.well-known/aauth-resource.json document
defined in AAuth Protocol ([I-D.hardt-oauth-aauth-protocol]) with two
fields:
Hardt Expires 1 April 2027 [Page 10]
Internet-Draft AAuth-Budgets September 2026
{
"issuer": "https://inference.example",
"jwks_uri": "https://inference.example/.well-known/jwks.json",
"access_mode": "auth-token",
"authorization_endpoint": "https://inference.example/authorize",
"scope_descriptions": {
"inference.completions": "Generate completions,
billed to your account"
},
"budget_units": [
{ "unit": "USD", "decimals": 6, "max": 10000000 },
{ "unit": "tokens", "decimals": 0, "max": 5000000 }
],
"usage_endpoint": "https://inference.example/usage",
"revocation_endpoint": "https://inference.example/revoke"
}
*budget_units* (OPTIONAL). An array of objects, each declaring one
unit the resource will denominate a budget in. Each object contains:
* *unit* (REQUIRED). The unit identifier.
* *decimals* (REQUIRED). The scale the resource uses for this unit.
A resource MUST use this value in every budget it issues for this
unit.
* *max* (RECOMMENDED). A non-negative integer, in this unit's
scale, giving the largest amount the resource will accept on a
single auth token. It lets an agent on the proactive path request
something the resource will honor rather than discovering the
ceiling by having its request narrowed.
* *description* (OPTIONAL). A Markdown string describing what the
unit meters, for display at a consent screen. Implementations
MUST sanitize the Markdown before rendering to users.
A resource that declares budget_units MUST NOT issue a resource token
whose budget.unit is absent from the array, and MUST set
budget.decimals to the value declared for that unit.
*usage_endpoint* (OPTIONAL). The HTTPS URL where a person server or
access server queries usage counters Section 15. The URL MUST
conform to the requirements the base protocol places on endpoint URLs
([I-D.hardt-oauth-aauth-protocol], Endpoint and Other URLs).
The example also carries revocation_endpoint, which the base protocol
recommends for a resource that accepts person tokens, as a metered
resource does ([I-D.hardt-oauth-aauth-protocol], Resource Metadata).
Settlement of a revoked token depends on it Section 14.4.1.
Hardt Expires 1 April 2027 [Page 11]
Internet-Draft AAuth-Budgets September 2026
A PS or AS that receives a resource token carrying budget already
fetches {iss}/.well-known/aauth-resource.json to discover the
resource's JWKS, per the dwk claim
([I-D.hardt-httpbis-signature-key]). The unit declarations arrive in
a fetch it was already making, so the cross-check in Section 17 costs
no extra round trip.
6. Authorization Endpoint Extensions
An agent MAY include a budget object Section 4.5 in the authorization
endpoint request body alongside scope:
POST /authorize HTTP/1.1
Host: inference.example
Content-Type: application/json
AAuth-Capabilities: interaction, budget
Signature-Input: sig=("@method" "@authority"
"@path" "signature-key");created=1754611200
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwt;jwt="eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiYWEtcGVyc29uK2p3dCIsImtpZCI6InBzLWtleS0xIn0..."
{
"scope": "inference.completions",
"budget": { "amount": 10000000, "unit": "USD", "decimals": 6 }
}
The agent presents a person token at the authorization endpoint, as
the base protocol requires ([I-D.hardt-oauth-aauth-protocol],
Authorization Endpoint).
*budget* (OPTIONAL). The ceiling the agent is requesting. All three
members of the budget object are REQUIRED when budget is present.
The resource MUST NOT reject the request because budget.amount
exceeds what it will grant; it narrows instead Section 4.2. The
resource MAY reject a budget whose unit it has not declared
Section 17.
When the agent obtains its resource token from a 401 challenge rather
than the authorization endpoint ([I-D.hardt-oauth-aauth-protocol],
Auth Token Required), it has not stated a budget and the resource
sizes the resource token on its own. This is why max in budget_units
matters: it is what makes the proactive path useful on a first
attempt.
How an agent knows an operation is metered before its first call is
answered by R3 ([I-D.hardt-aauth-r3]): a resource MAY annotate
individual operations in its vocabulary with a budget annotation, and
Hardt Expires 1 April 2027 [Page 12]
Internet-Draft AAuth-Budgets September 2026
an agent that reads one knows to include budget in this request. The
annotation states the fact of metering; budget_units states the units
and ceilings.
7. Resource Token Extensions
This document extends the resource token (a JWT with typ: aa-
resource+jwt) with two optional claims.
* *budget* (OPTIONAL): A budget object Section 4.5. The ceiling the
resource is willing to have granted. Subject to the rules in
Section 5.
* *budget_consumed* (OPTIONAL): A consumption record Section 7.1 for
the auth token presented on the request this resource token
answers — what it has consumed to date. Not a grant.
{
"iss": "https://inference.example",
"dwk": "aauth-resource.json",
"aud": "https://ps.example",
"jti": "rt-4c81fa",
"ps": "https://ps.example",
"sub": "8f14e45fceea167a5a36dedd4bea2543",
"presented_jti": "at-71b9d0",
"agent_jkt": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs",
"tenant": "corp",
"mission_s256": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
"scope": "inference.completions",
"budget": { "amount": 5000000, "unit": "USD", "decimals": 6 },
"budget_consumed": { "jti": "at-71b9d0", "consumed": 2000000 },
"iat": 1754612400,
"exp": 1754612700
}
This example is the resource token a resource issues on a challenge
to a request presenting the auth token of Section 10 once that
token's grant is spent, so presented_jti and the record both name
that token.
7.1. The Consumption Record
budget_consumed is a single consumption record with two members, both
REQUIRED:
* *jti*: The jti claim of the auth token presented on the request
this resource token answers. This is the value the resource token
carries as presented_jti ([I-D.hardt-oauth-aauth-protocol],
Resource Token Structure).
Hardt Expires 1 April 2027 [Page 13]
Internet-Draft AAuth-Budgets September 2026
* *consumed*: What the resource has metered against that token's
budget, as of this resource token's iat: a non-negative integer in
the unit and decimals of the budget claim in the same resource
token.
A resource MUST NOT include budget_consumed unless budget is present
in the same token, and MUST omit it rather than state it in another
scale — a record carried in a stale scale is the thousandfold error
Section 17 in miniature.
The record exists for one figure the issuer needs and cannot compute.
budget-exhausted implies the whole grant was spent, but under
insufficient-budget Section 13, and on a token that expired with
budget left, the token's spend to date is a number only the resource
holds — and without it the issuer accounts for the allocation as
fully consumed Section 14.4 when most of it may remain. A resource
issuing a resource token on a challenge to a request that carried an
auth token SHOULD include the record. A resource issuing one on a
person token, at its authorization endpoint or on the first challenge
of a grant, has no auth token to report on and omits the claim. In
four-party access the resource token travels to the AS inside the
PS's token request, so the record reaches both issuers without a
usage query.
The record is as of the resource token's iat. It is a snapshot: the
token it names is still valid, since a resource issues a resource
token only on a request carrying a valid person token or auth token,
and the token may spend more before it expires. A later record for
the same jti supersedes an earlier one. A token's final figure is
settled by the usage reading Section 14.4.1.
A record is deliberately two members and no more. The jti it names
is a token the PS issued — or, in four-party access, relayed from the
AS — so the issuer already holds the granted amount, the mission, the
scope, and the issuance time, and joins them from its own ledger.
Carrying those values again would duplicate what the issuer knows and
put more of the person's financial detail into a token the agent also
reads. What the issuer cannot know, and what the record supplies, is
what the resource actually metered.
Hardt Expires 1 April 2027 [Page 14]
Internet-Draft AAuth-Budgets September 2026
The record rides in the resource token because it already travels
resource → agent → PS at exactly the moment the PS re-decides: no
extra round trip, resource-signed, and interpretable without a
metadata fetch. It names the presented token and nothing else. The
spend under a person's other tokens is served at the usage endpoint
Section 15, which answers that question better — per key, per
mission, over calendar periods — on a channel the agent is not on.
An earlier revision carried up to twenty records; Appendix A.4 says
why one is enough.
8. Auth Token Endpoint Extensions
No new request parameter is defined for the agent's request to the
PS's auth_token_endpoint. The budget reaches the PS inside the
resource token.
An agent seeking a larger budget obtains a fresh resource token from
the resource — stating the larger figure in the authorization
endpoint request Section 6, or being handed one on a 401 Section 13 —
and SHOULD explain the need in the justification parameter of the
auth token request.
When the PS issues the auth token itself (three-party), it applies
the person's policy and issues per Section 10. When it federates
(four-party), it proceeds per Section 9.
8.1. What the PS Is Deciding
The resource token's budget states what the resource will allow. It
is an offer, not a request the PS is obliged to answer in full.
Against that offer the PS holds a ceiling for the person at this
resource — a standing limit, a mission's stated intent, an
organizational policy, or a figure the person supplied when asked.
The ceiling is PS state. This document defines no wire format for
it, no claim that carries it, and no way for the agent to read it.
What the PS issues is an allocation drawn against it.
Sizing the allocation is where the PS's supervision happens. A PS
that issues the resource's full offer every time has authorized the
resource's maximum and learns nothing until the money is gone. A PS
that issues a fraction sees the agent again when that fraction is
spent, with the token's consumption record in hand, and decides then
whether the work is going as the person expected.
The interval is not fixed by the clock. An auth token expires within
an hour, and its budget is exhausted after however much work it took
to spend — whichever comes first returns the agent to the PS. A
Hardt Expires 1 April 2027 [Page 15]
Internet-Draft AAuth-Budgets September 2026
mission running cheaply reports on the hour; one running expensively
reports in minutes. The PS sets that frequency by sizing the
allocation, and no party configures it Section 14.3.
8.2. What the PS Reads
Four inputs are available at the moment of the decision, and a PS
applying the person's policy SHOULD use all of them:
* *budget_consumed* Section 7.1 in the resource token the agent just
presented: what the token it was presenting has cost so far,
resource-signed, arriving at no round-trip cost.
* *Usage counters* Section 15 at the resource's usage_endpoint:
totals over calendar periods, and for a mission query the
mission's total to date — the figures that cover the stretch when
the agent was not talking to the PS.
* *The mission log* ([I-D.hardt-oauth-aauth-protocol], Mission Log):
every prior token request, justification, and clarification in
this mission, which is what makes "faster than expected" a
judgement the PS can actually make.
* *The justification* parameter of this request: why the agent says
it needs more.
The first two are the spend; the second two are the context. A
budget escalation is not interpretable without both.
8.3. How the PS Responds
Six responses are available. None is new to this document; the base
protocol defines each, and this section states which apply to a
budget decision.
Hardt Expires 1 April 2027 [Page 16]
Internet-Draft AAuth-Budgets September 2026
+=================+=======================================+
| Response | Mechanism |
+=================+=======================================+
| Grant the offer | Issue an auth token with budget equal |
| | to the resource token's Section 10 |
+-----------------+---------------------------------------+
| Grant less | Issue a lower amount Section 4.2 |
+-----------------+---------------------------------------+
| Ask the agent | 202 with requirement=clarification |
+-----------------+---------------------------------------+
| Ask the person | 202 with requirement=interaction |
+-----------------+---------------------------------------+
| Decline with a | Error response carrying |
| figure | suggested_budget Section 17.1 |
+-----------------+---------------------------------------+
| End the work | Terminate the mission |
+-----------------+---------------------------------------+
Table 2
Granting less needs no signalling: the amount in the issued claim is
the answer, and the agent reads it from the token it received
Section 4.2.
*Clarification is the response for an escalation the PS is not ready
to refuse or approve.* A PS that sees consumption running ahead of
what the mission implies MAY return 202 with
requirement=clarification ([I-D.hardt-oauth-aauth-protocol],
Clarification Required), putting a question to the agent before
deciding. This is the channel that lets the PS tell an agent it is
overspending, which narrowing alone cannot do — a smaller amount is
silent, and the agent cannot distinguish a PS applying pressure from
a resource lowering its own offer.
HTTP/1.1 202 Accepted
Location: /pending/abc123
Retry-After: 0
Cache-Control: no-store
AAuth-Requirement: requirement=clarification
Content-Type: application/json
{
"status": "pending",
"clarification": "This mission has spent $18 of an
expected $25 and has not booked anything yet. What
is the remaining $12 for?",
"timeout": 120
}
Hardt Expires 1 April 2027 [Page 17]
Internet-Draft AAuth-Budgets September 2026
The agent's three replies are already defined and all three are
useful here: a clarification_response explaining the spend, an
updated_request carrying a fresh resource token for a smaller figure
with the presented_token it used to obtain it
([I-D.hardt-oauth-aauth-protocol], Updated Request), or a DELETE
withdrawing the request. The base protocol's limit on clarification
rounds applies ([I-D.hardt-oauth-aauth-protocol], Clarification
Limits). An agent that did not declare the clarification capability
cannot be asked, and the PS decides without it.
Asking the person is the same mechanism one step further out, and is
the right response when the answer is the person's rather than the
agent's — a ceiling raise rather than an allocation.
A PS that puts a budget to the person for consent MUST present the
amount as a human-readable figure in its unit — "$5.00", not
{5000000, USD, 6} — visually distinct from any resource-supplied
description, which is Markdown and MUST be sanitized before rendering
Section 5. The amount is the decision the person is making.
Ending the work is the response to an agent whose spending the PS
cannot account for. Terminating a mission is not a budget mechanism
and this document defines nothing about it; it is named here because
a budget escalation is one of the few signals that reliably surfaces
an agent behaving unlike its mission.
9. PS-to-AS Token Request Extensions
This document extends the PS-to-AS token request
([I-D.hardt-oauth-aauth-protocol], PS-to-AS Token Request) with one
parameter, used in four-party access only.
*budget* (OPTIONAL): A budget object Section 4.5 carrying the ceiling
the PS will allow.
Hardt Expires 1 April 2027 [Page 18]
Internet-Draft AAuth-Budgets September 2026
POST /token HTTP/1.1
Host: as.inference.example
Content-Type: application/json
Content-Digest: sha-256=:...:
Signature-Input: sig=("@method" "@authority" "@path"
"content-type" "content-digest"
"signature-key");created=1754611200
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwks_uri;id="https://ps.example";
dwk="aauth-person.json";kid="key-1"
{
"resource_token": "eyJhbGc...",
"agent_token": "eyJhbGc...",
"presented_token": "eyJhbGc...",
"budget": { "amount": 2000000, "unit": "USD", "decimals": 6 }
}
The PS MUST copy unit and decimals from the resource token's budget
claim unchanged, and MUST NOT set amount higher than the resource
token's budget.amount. The AS MUST NOT issue a budget claim
exceeding this parameter, and MAY lower it further.
When the AS returns an auth token, the PS MUST verify, along with the
checks of the base protocol's Auth Token Delivery, that the token
carries a budget claim when the PS sent a budget parameter, that the
claim has the unit and decimals of that parameter and an amount no
higher, and that the token carries no budget claim when the PS sent
no budget parameter. A token without a budget claim would leave the
resource to apply its own default, which can exceed what the PS
allowed. A token that fails is an auth token that fails delivery
verification, and the PS answers the agent as_unreachable
([I-D.hardt-oauth-aauth-protocol], Auth Token Delivery). This is the
budget counterpart of the base protocol's check that the AS's scope
is no broader than the resource token's.
When the resource token carries budget and the PS omits this
parameter, the AS MUST NOT issue a budget claim. A PS that grants
the resource's full offer says so by echoing the resource token's
budget; omission is what a PS that does not implement this extension
sends, and it MUST NOT be read as a grant. The auth token then
carries no allocation, and the resource applies its own default to it
Section 10. Reading omission as the full offer would turn a PS's
non-participation into the maximum grant, the opposite of what
ignoring an unrecognized claim is meant to do.
Hardt Expires 1 April 2027 [Page 19]
Internet-Draft AAuth-Budgets September 2026
10. Auth Token Extensions
This document extends the auth token (a JWT with typ: aa-auth+jwt)
with one optional claim.
*budget* (OPTIONAL): A budget object Section 4.5. This is the
granted budget and is authoritative.
{
"iss": "https://ps.example",
"dwk": "aauth-person.json",
"aud": "https://inference.example",
"jti": "at-71b9d0",
"ps": "https://ps.example",
"sub": "8f14e45fceea167a5a36dedd4bea2543",
"cnf": { "jwk": { "kty": "OKP", "crv": "Ed25519",
"x": "NzbLsXh8uDCcd...", "alg": "Ed25519" } },
"tenant": "corp",
"mission_s256": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
"scope": "inference.completions",
"budget": { "amount": 2000000, "unit": "USD", "decimals": 6 },
"iat": 1754611200,
"exp": 1754614800
}
Issuer rules:
* The issuer MUST copy unit and decimals from the resource token's
budget claim unchanged.
* The issuer MUST NOT set amount higher than the resource token's
budget.amount, or, in four-party access, higher than the budget
parameter of the PS-to-AS token request Section 9.
* An issuer MUST NOT include budget in an auth token when the
resource token carried no budget claim. The resource, not the PS
or AS, denominates.
Resource rules:
* A resource that issued a budget in a resource token and receives
an auth token without a budget claim MUST treat the request as
carrying no budget authorization under this extension and apply
its own default.
* A resource MUST enforce against the auth token's unit and
decimals, not against its own current budget_units metadata. Auth
tokens live up to an hour and metadata can change within that
hour; see Appendix A.3.
Hardt Expires 1 April 2027 [Page 20]
Internet-Draft AAuth-Budgets September 2026
11. AAuth-Budget Response Header
AAuth-Budget is a response header carrying the remaining balance of
the granted budget. It is a Dictionary ([RFC9651], Section 3.2),
matching AAuth-Requirement.
AAuth-Budget: cost=221200, remaining=1568800,
unit="USD", decimals=6
Members:
* *remaining* (REQUIRED): A non-negative Integer, in the granted
scale, giving what is left of the budget on this auth token, net
of reservations for requests in flight Section 14.8. It is a
floor — committed consumption will not exceed the grant — though
the figure may lag metering. Exhaustion is signaled by the
requirement=auth-token challenge, as a 401 or a 202 Section 13,
for which the agent stays prepared regardless.
* *cost* (OPTIONAL): A non-negative Integer, in the granted scale,
giving what *this request* cost. A resource sends it in the
header when it knows the figure as it writes the response, in a
trailer when it learns the figure after Section 12, and not at all
when it will not learn it in time to do either. The third case is
bounded by Section 12.1.
* *reserved* (OPTIONAL): A non-negative Integer, in the granted
scale, giving what the resource has held against the grant for
this request and not yet committed Section 14.8. Meaningful only
where cost is not yet known, so in practice it accompanies a
streamed response. It is a statement about this request, not a
running total, and is never revised. REQUIRED where cost is
omitted Section 12.1.
* *required* (OPTIONAL): A non-negative Integer, in the granted
scale, giving the maximum cost the resource computed for a request
it refused under reason=insufficient-budget Section 13.1. Sent
only with that refusal, where it is RECOMMENDED. It is what the
request needed, not what the resource is asking the PS to grant
next; see Section 13.2.
* *unit* (OPTIONAL): A String naming the unit. *decimals*
(OPTIONAL): an Integer giving its scale. Both are informational,
and they are a pair: a sender MUST include both or neither. They
exist for readers that never parse a JWT — proxies, logs,
dashboards. Those are the same readers that would misinterpret an
amount carrying a unit with no scale, by a factor of 10^decimals.
Recipients MUST ignore members they do not recognize.
Hardt Expires 1 April 2027 [Page 21]
Internet-Draft AAuth-Budgets September 2026
The header carries no granted member, no cumulative consumption
figure, and no token reference. The agent holds the auth token it
signed the request with and reads granted from there; what it needs
per response is what this call cost and what is left, which is what
the field carries. Cumulative consumption is an issuer-facing
figure, reported in the resource token Section 7.1 and at the usage
endpoint Section 15; see Appendix A.5 for why it is not also reported
to the agent.
The scope of the reported figures is this auth token's budget,
because the budget expires with the token Section 14.
11.1. Sending Rules
A resource that granted a budget SHOULD include AAuth-Budget on every
response to a request bearing that auth token — success, error, and
the challenge, 401 or 202 Section 13.3, where it reads remaining=0
beside the AAuth-Requirement header Section 13.
This is SHOULD rather than MUST because the failing layer may sit
below the metering layer: a gateway timeout, a crashed worker, or a
fault in metering itself produces a response no budget figure can
ride on. A resource MUST NOT omit the field for any other reason.
An agent that misses the field learns the balance from its next
response, and until then applies Section 12.3.
HTTP/1.1 401 Unauthorized
AAuth-Requirement: requirement=auth-token;
resource-token="eyJ..."; reason=budget-exhausted
AAuth-Budget: remaining=0, unit="USD", decimals=6
That the field appears on every response regardless of status is the
point of putting it in a header. The agent reads the same field
whether the body is JSON, a server-sent event stream, a streamed
completion, or a problem document [RFC9457].
Intermediaries MUST NOT add, alter, or remove AAuth-Budget. The
field reports the state of an authorization the resource issued; an
intermediary rewriting it is asserting authorization state it does
not hold. This is the inverse of the RateLimit rule permitting
intermediaries to tighten values
([I-D.ietf-httpapi-ratelimit-headers]) — see Appendix A.8.
Recipients MUST ignore AAuth-Budget on a response served from cache
with a positive current_age ([RFC9111], Section 4.2.3). This is the
one RateLimit rule that carries over unchanged.
Hardt Expires 1 April 2027 [Page 22]
Internet-Draft AAuth-Budgets September 2026
11.2. Denomination Conflict
The auth token's budget claim is authoritative. A recipient MUST NOT
act on a header unit or decimals that disagrees with the budget claim
in the auth token it presented, and SHOULD treat the whole field as
unreliable for that response.
Including the pair makes the field self-describing for proxies and
logs that never parse a JWT, at the cost of duplicating signed
values; the conflict rule is that cost made explicit.
12. Streaming
Response headers are written before the body, and a streamed
response's actual cost is known only when the stream ends. The
resource therefore cannot state cost in the header. What it can
state is what it has held: it reserved before serving Section 14.8,
and remaining is already net of that reservation.
A resource serving a streamed response SHOULD send reserved in the
header and cost in a trailer. A resource that cannot send trailers
sends reserved alone Section 12.1.
HTTP/1.1 200 OK
Content-Type: text/event-stream
Trailer: AAuth-Budget
AAuth-Budget: remaining=1568800, reserved=431200,
unit="USD", decimals=6
...stream...
AAuth-Budget: cost=221200
The agent computes the balance after the request as remaining +
reserved - cost. Here that is 1,778,800: the 431,200 held was not
all spent, and the unspent 210,000 returns to the grant.
12.1. When cost Is Omitted
Not every resource can send a trailer. Trailers exist only on a
chunked or HTTP/2-and-later response, and several widely deployed
server runtimes provide no way to emit one at all. A resource in
that position knows the cost of a streamed response only after its
last opportunity to report it.
Such a resource omits cost and MUST send reserved in the header. The
agent recovers the figure from the following response:
Hardt Expires 1 April 2027 [Page 23]
Internet-Draft AAuth-Budgets September 2026
cost = previous remaining + reserved - current remaining
The subtraction works because remaining is already net of
reservations Section 11: the earlier figure is net of the hold, the
later one reflects the commit and the release of the unspent
remainder. reserved is the term that connects them, which is why it
stops being optional here.
It recovers one request's cost only where requests on that token are
serial. An agent with several requests in flight on one token
recovers the net of everything that settled between the two
responses, not the cost of any single one, because every concurrent
request moves the same remaining. An agent that wants per-request
figures from a resource that omits cost serializes its requests on
that token; an agent that only needs the balance does not have to.
Until that next response arrives the agent applies Section 12.3 and
treats the request as having cost the full reserved amount. That is
the conservative direction, and it is the same rule the agent already
applies to a response it never received.
A resource MUST NOT omit both cost and reserved. That combination
reports that a metered request happened and gives the agent no figure
for it, neither exact nor conservative.
12.2. Trailer Rules
A resource MAY send AAuth-Budget as a trailer field, subject to three
rules:
1. The response MUST list AAuth-Budget in a Trailer header field
([RFC9110], Section 6.6.1).
2. A trailer instance MUST NOT restate a member the header instance
carried. It carries cost and nothing else.
3. A recipient MUST NOT treat the trailer as necessary. A response
that never delivers one is complete.
Rule 2 is what makes the field safe under either way a recipient
handles trailers. A recipient that discards trailers keeps the
header's remaining, which is a floor and therefore correct if
conservative. A recipient that merges trailer fields into the header
set produces a single Dictionary whose keys do not collide, because
no key appears twice. Restating a member is the case that would
break: AAuth-Budget is a Dictionary, duplicate keys resolve last-wins
([RFC9651], Section 3.2), and which value won would then depend on
whether the recipient merged — a difference no sender can observe or
control.
Hardt Expires 1 April 2027 [Page 24]
Internet-Draft AAuth-Budgets September 2026
Rule 3 follows from trailers being droppable in transit ([RFC9110],
Section 6.5.2) and from trailers existing only on a chunked or
HTTP/2-and-later response. The figure a trailer would have carried
is also in the next response's remaining, so nothing is lost that is
not recovered on the following call.
12.3. Ambiguous Failures
A request whose response never arrives — a dropped connection, an
aborted stream — leaves the agent unable to say whether it was
metered. No trailer arrives on an aborted stream, and the header
that would have carried cost is on the response that was lost.
The agent MUST assume the request cost as much as the resource had
held for it: reserved where it saw one, and otherwise the request's
maximum cost as the resource would have bounded it Section 14.8. It
carries that assumption until a later response's remaining supersedes
it, and it SHOULD NOT retry a chargeable request before then.
Assuming the maximum is the conservative direction: an agent that
under-assumes plans spending it does not have and discovers the
shortfall as a 401 Section 13.
Inference APIs commonly emit final usage in the stream's terminal
event. That is application-layer and does not provide the
application independence this header exists for, so a resource that
emits it and can also send a trailer SHOULD send both. What it does
mean is that the exact number exists when the stream ends, and a
resource whose runtime offers no trailer has it and no protocol
carrier for it until the next response Section 12.1.
13. Budget Exhaustion
*Auth token expired.* The budget expires with the token. The base
protocol has the agent refresh an auth token when fewer than five
minutes remain and not present it inside that margin
([I-D.hardt-oauth-aauth-protocol], Expiry and the Refresh Margin).
An expired token presented anyway is answered 401 with Signature-
Error: error=expired_jwt, and the resource issues no resource token.
Either way the agent re-authorizes with a person token at the
authorization endpoint ([I-D.hardt-oauth-aauth-protocol], Re-
authorization). No consumption record rides on that path, and an
allocation that expired unreported settles from the usage reading
Section 14.4.1.
*Budget exhausted, token still valid.* 401 with AAuth-Requirement:
requirement=auth-token; resource-token="...". The base protocol
already permits a resource to return requirement=auth-token with a
Hardt Expires 1 April 2027 [Page 25]
Internet-Draft AAuth-Budgets September 2026
new resource token to a request that already carries an auth token,
when the request needs more authorization than the token provides,
and requires agents to be prepared for this step-up at any time
([I-D.hardt-oauth-aauth-protocol], Auth Token Required). Budget
exhaustion is that case, and the agent's action is the same as for
any step-up: take the fresh resource token to its PS, with the
exhausted auth token as presented_token. The resource token's
consumption record Section 7.1 is the exhausted token's spend to
date. The auth token issued against it expires no later than the
exhausted one ([I-D.hardt-oauth-aauth-protocol], Auth Token
Structure), so a step-up renews the allocation but not the token's
lifetime. A full-length token comes from re-authorizing with a fresh
person token.
*Request exceeds the remainder.* The budget has remainder, but this
request's maximum cost exceeds it Section 14.8. The same 401
challenge, with reason=insufficient-budget. The agent has a second
move here that exhaustion does not offer: lower the request's bound
to fit the remaining reported beside the challenge, and retry on the
token it already holds. The required member Section 13.2 is what
makes that move a calculation rather than a search.
A request refused under this section MUST NOT draw down the budget or
appear in the record and counters. The resource declined to serve
it; metering the refusal would make exhaustion self-perpetuating. A
refusal therefore carries no cost; required Section 13.2 is the
figure a refusal reports.
The fresh resource token SHOULD carry the presented token's
consumption record Section 7.1, its spend to date — the context for
deciding whether to authorize more, and the figure that tells the
issuer how much of the refused allocation was actually consumed.
13.1. The reason Parameter
A resource challenging because the budget is exhausted rather than
because the token expired SHOULD include a reason parameter on the
requirement member:
HTTP/1.1 401 Unauthorized
AAuth-Requirement: requirement=auth-token;
resource-token="eyJ..."; reason=budget-exhausted
AAuth-Budget: remaining=0,
unit="USD", decimals=6
reason is a Token. This document defines two values:
* *budget-exhausted*: The granted budget is spent.
Hardt Expires 1 April 2027 [Page 26]
Internet-Draft AAuth-Budgets September 2026
* *insufficient-budget*: The budget has remainder, but this
request's maximum cost exceeds it Section 14.8.
For either value, the enclosed resource token MAY carry a budget
sized for what the resource would need to see granted. The denial is
itself the re-authorization offer.
13.2. Refusing a Request That Does Not Fit
A resource refusing under insufficient-budget has computed the
request's maximum cost — Section 14.8 requires it to, before serving
— and SHOULD report that figure as the required member of AAuth-
Budget:
HTTP/1.1 401 Unauthorized
AAuth-Requirement: requirement=auth-token;
resource-token="eyJ..."; reason=insufficient-budget
AAuth-Budget: remaining=150000, required=400000,
unit="USD", decimals=6
The agent now knows both halves of the refusal: it has 0.15, and the
request needed 0.40. Without required it knows only the first, and
the move Section 13 offers it — lower the request's bound and retry
on the token it already holds — becomes a search. It cannot compute
the figure itself, because the bound is the resource's own
calculation against its own pricing, and no part of this
specification requires a resource to publish what an operation costs.
required is not the same figure as the budget claim of the enclosed
resource token, and the two SHOULD differ. The resource token's
budget is a re-authorization offer addressed to the PS, and a
resource sizing it for exactly the refused request hands back a grant
good for one call. required is a fact about the request that was
refused, addressed to the agent. The header carries it because the
agent is the party that acts on it, and because the retry path it
enables does not involve the person server at all.
A resource MAY refuse without required — where the operation has no
cost bound it is willing to state, or where stating it would disclose
pricing the resource does not publish. The agent then falls back to
remaining alone.
No new requirement value is minted. The base protocol says an agent
that does not recognize a requirement value MUST NOT treat the
response as satisfiable and surfaces it as an error, while recipients
MUST ignore unknown _parameters_ on the requirement member
([I-D.hardt-oauth-aauth-protocol], Requirement Responses). A new
value would hard-fail every budget-unaware agent on a condition that
Hardt Expires 1 April 2027 [Page 27]
Internet-Draft AAuth-Budgets September 2026
plain auth-token resolves correctly. That asymmetry — unknown values
fail, unknown parameters are ignored — is why this extension extends
by parameter.
An agent that understands the reason values knows what to do beyond
re-authorizing: for budget-exhausted, request a larger budget and say
why in the justification it sends to its PS; for insufficient-budget,
either that, or shrink the request and retry without involving the PS
at all. An agent that understands neither ignores the parameter and
re-authorizes, which is always correct.
13.3. Deferred Delivery
A resource MAY deliver either refusal as a 202 deferred response
rather than a 401, holding the invocation
([I-D.hardt-oauth-aauth-protocol], Deferred Delivery). The AAuth-
Requirement header is the same, reason included, and so is the
enclosed resource token with its consumption record Section 7.1.
What differs is what becomes of the request. Under the 401 the
resource holds nothing, and the agent sends the request again. Under
the 202 the resource holds the invocation, and the agent does not
send it again: it obtains an auth token and polls the pending URL,
and the resource executes the held invocation on the first poll that
presents a valid auth token.
HTTP/1.1 202 Accepted
Location: /pending/f7a3b9c
Retry-After: 5
Cache-Control: no-store
AAuth-Requirement: requirement=auth-token;
resource-token="eyJ..."; reason=budget-exhausted
AAuth-Budget: remaining=0, unit="USD", decimals=6
Content-Type: application/json
{
"status": "pending"
}
A resource holding an invocation under this section follows four
rules:
1. *Nothing is reserved while it is held.* For the held invocation,
the resource MUST NOT reserve against or meter to the token
presented on the original request. For that token the 202 is a
refusal like the 401, and any remainder it has stays available to
the agent's other requests.
Hardt Expires 1 April 2027 [Page 28]
Internet-Draft AAuth-Budgets September 2026
2. *It meters against the completing token.* The held invocation is
metered against the auth token presented on the poll that
completes it. The resource applies Section 14.8 to that token
before executing: it reserves the invocation's maximum cost
against that token's remainder, serves, and commits.
3. *A completing token that does not fit does not complete it.* If
the invocation's maximum cost exceeds the remainder of the token
presented on the poll, the resource does not execute it. It
answers the poll with another 202 carrying requirement=auth-
token, reason=insufficient-budget, and a fresh resource token
whose presented_jti names that token, and continues to hold the
invocation.
4. *A replay is not metered again.* A resource answering a repeated
presentation of the completing token from the stored result, as
the base protocol requires, MUST NOT meter the invocation a
second time. The AAuth-Budget on the replay reports that token's
current remaining, so that the figure is still a floor, with the
invocation's cost where the resource has it and no reserved.
AAuth-Budget on the 202 reports what it would report on the 401: the
remaining of the token presented on the request, required under
insufficient-budget Section 13.2, and unit and decimals. It carries
no cost and no reserved, because nothing was served or held against
that grant. A later 202 answering a poll that bore an auth token
reports on that token the same way Section 11.1. The response to the
completing poll carries AAuth-Budget for the completing token, as any
response to a request bearing it does.
Under insufficient-budget, the agent's other move — lower the
request's bound and retry on the token it already holds Section 13 —
abandons the pending URL. The agent sends a new request with the
smaller bound and does not poll the pending URL again. The held
invocation is never executed, and since nothing was reserved for it
(rule 1), abandoning it costs the grant nothing. The resource
discards it when the pending URL's lifetime ends
([I-D.hardt-oauth-aauth-protocol], Pending URL Security).
13.4. Boundaries
429 Too Many Requests is not used by this document. The base
protocol gives it two meanings already — slow_down on a pending URL,
which asks a polling agent to lengthen its interval, and rate_limited
at a revocation endpoint, which refuses work a caller has sent too
much of ([I-D.hardt-oauth-aauth-protocol]). Neither is a budget
condition. A budget refusal is about what the next request would
cost, not about how often requests arrive, and the agent answers it
by re-authorizing rather than by waiting.
Hardt Expires 1 April 2027 [Page 29]
Internet-Draft AAuth-Budgets September 2026
402 Payment Required is a different condition: the resource needs
payment rather than re-authorization from the person. The base
protocol already permits AAuth-Requirement on a 402, and this
document does not change that.
14. Enforcement
14.1. The Resource Enforces
The PS authorizes a number. The resource counts. The PS is not in
the request path and does not meter.
14.2. Budgets Require Auth-Token Mode
A budget is carried in the budget claim of an auth token, so a
resource can enforce one only where it requires auth tokens. A
resource that declares access_mode: person-token and serves requests
on the person's identity alone ([I-D.hardt-oauth-aauth-protocol],
Person Identity Access) has no auth token to read a budget from, and
neither does a resource operating in agent-token or session-token
mode.
A metered resource therefore requires an auth token on the endpoints
it meters (access_mode: auth-token). It MAY continue to serve
unmetered endpoints on a person token, since a resource MAY apply
different modes to different endpoints
([I-D.hardt-oauth-aauth-protocol], Resource Access Modes). A
resource MUST NOT rely on this extension for an endpoint it serves
without an auth token.
Where the resource holds the authorization state itself rather than
reading it from a signed claim, there is nothing for the person
server to have bounded.
14.3. Token Scope
A budget is scoped to the auth token that carries it and expires with
it. There is no persistent grant identifier and no requirement that
the PS carry a budget across re-issuance. This is the mechanism, not
a gap: re-issuance is where the PS re-decides Section 8.1, and a
budget that survived it would be a standing grant the PS no longer
sizes.
The budget is revoked with the token. Revoking an auth token at its
resource ends its budget along with the rest of its authorization.
In three-party access the PS revokes it there; in four-party access
the PS revokes the person token at the AS, and the AS revokes the
auth tokens it issued against it ([I-D.hardt-oauth-aauth-protocol],
Hardt Expires 1 April 2027 [Page 30]
Internet-Draft AAuth-Budgets September 2026
Token Revocation). A resource without a revocation endpoint honors a
revoked token until its exp. Consumption already committed is
unaffected — a budget is a ceiling on spending, not a claim on what
was spent — and a request already in flight completes, because
revocation stops a token being used again rather than interrupting a
call. This document adds nothing to that mechanism; it is named here
because a person hitting stop expects the money to stop, and expiry
alone bounds that at an hour. What a revocation means for the
issuer's own accounting — when the allocation it reserved can be
released — is Section 14.4.1.
Two conditions return the agent to the PS, and either is sufficient.
The auth token nears its exp: the base protocol caps an auth token at
one hour and has the agent refresh it when fewer than five minutes
remain ([I-D.hardt-oauth-aauth-protocol], Expiry and the Refresh
Margin). Or its budget is exhausted Section 13, which happens after
however much work it took to spend. Expiry is proportional to time
and exhaustion is proportional to spend, so the supervision interval
tracks whichever is moving faster: a mission running cheaply reports
on the hour, one running expensively reports in minutes, and no party
configures the difference.
14.4. Unreported Allocations
An allocation can expire before its issuer sees a figure for it. A
consumption record comes back only on a challenge to a request that
presented the token Section 7.1. A token the agent retires by
refreshing it, as the base protocol has it do inside the refresh
margin Section 13, is never named in one, and neither is a token held
by an agent that has crashed or been abandoned. Until the next usage
query, the issuer knows a grant was live and nothing about what it
consumed — the true figure is anywhere from zero to the full amount.
Until a figure arrives, the issuer MUST account for the allocation as
fully consumed. The error modes are not symmetric: an issuer that
assumes less than was spent sizes the next allocation against
headroom that may not exist and can overrun the ceiling it holds,
while one that assumes the maximum is at worst temporarily
conservative, which the next figure corrects. This is Section 12.3
applied to the other end of the grant — the party missing a figure
assumes the maximum until a real one supersedes it.
Hardt Expires 1 April 2027 [Page 31]
Internet-Draft AAuth-Budgets September 2026
Reconciliation is idempotent where the channel names the token. A
consumption record is a {jti, consumed} pair whose consumed is that
token's total as of the resource token that carried it Section 7.1,
so a record arriving late — or arriving again — replaces the
assumption for that token rather than adding to it. Usage counters
and per-key figures Section 15 name no jti; they are aggregates the
issuer reconciles against its own ledger of what it assumed.
The rule falls on every party that sized the allocation against a
ceiling it holds. In three-party access that is the PS Section 8.1.
In four-party access it is also the AS, which issues against the
ceiling the PS stated Section 9 and may apply credit or risk limits
of its own Section 4.2. Both are entitled callers of the usage
endpoint Section 15.3.
14.4.1. Settlement
The issuer set every allocation's exp, so it knows when the
assumption can be settled.
A consumption record settles nothing. It is a snapshot Section 7.1:
the token stays valid, the agent may spend more on it, and under
insufficient-budget may retry a smaller request on it Section 13.
What the record gives the issuer is the spend so far, for the re-
authorization decision it is making at that moment.
A usage reading settles every allocation, in aggregate. The issuer
takes the person's figure from the usage endpoint Section 15,
complete through as_of, and then holds
free = ceiling − metered − Σ amount of every allocation not yet settled
where metered is the person's counter — all_time for a standing
ceiling, the matching calendar counter for a calendar one — and an
allocation is settled by the reading once its exp is at or before
as_of. A settled allocation needs no figure of its own: whatever it
consumed is inside metered, and it is no longer reserved. The only
over-count in free is consumption under still-live tokens, present in
both terms; it vanishes as each expires and the next reading covers
it. The per-token figure in a consumption record refines the
issuer's picture of a live allocation between readings; it does not
settle it.
A revoked token is not an early exp. Revocation stops the spending;
it does not by itself release the reservation, because the figure for
what was spent still arrives with the next reading. What it does is
move the moment after which no more can be spent, and the issuer
learns whether that moment exists from the revocation's own answer: a
Hardt Expires 1 April 2027 [Page 32]
Internet-Draft AAuth-Budgets September 2026
recipient answers 200 once its cascade is terminal, directly or at
the pending URL of a 202, and an AS reports each resource's outcome
in downstream ([I-D.hardt-oauth-aauth-protocol], Revocation
Response). Three outcomes, three answers:
* *Recorded at the resource.* Nothing further can be spent on that
token. The allocation is settled by the first usage reading whose
as_of is at or after the revocation was recorded, rather than the
first one at or after exp — the same rule as any other settlement,
since whatever was spent is inside metered by then.
* *revocation_unsupported.* The resource honors the token until exp.
The allocation stays reserved until exp and settles as it would
have without the revocation.
* *revocation_unavailable.* The issuer cannot tell which of the two
it has, so it holds the allocation reserved until exp and MAY
revoke again later.
An issuer that treated every revocation as an immediate settlement
would release headroom that a resource with no revocation endpoint is
still spending against, which is Section 14.4 in reverse and has the
error mode that section rules out.
An issuer whose ceiling is per calendar period SHOULD set each
allocation's exp no later than the period boundary. No allocation
then straddles two periods, every allocation of a period has expired
when the period ends, and the period settles without a query. The
cost is that a token issued near the boundary is short. The calendar
counters have no sub-day period Section 15.2.1; an issuer with an
hourly ceiling and clipped allocations never needs one, and an issuer
with a trailing window settles from differences of successive
all_time readings.
14.5. Aggregation
Two things are counted, against different keys, and they are not the
same requirement.
*The cap the resource enforces is per auth token.* It is the budget
claim of the token presented, and Section 14.8 states the invariant:
committed consumption plus outstanding reservations against _that
token_ MUST NOT exceed _its_ granted amount. A resource needs no
cross-token arithmetic to enforce a budget.
*The ledger the resource keeps is per person.* The resource MUST
aggregate consumption against the key (ps, sub, aud) of the auth
token, which is what the consumption record Section 7.1 and the usage
counters Section 15 report. (ps, sub) identifies the person: sub is
minted by the person server named in ps, and values from different
Hardt Expires 1 April 2027 [Page 33]
Internet-Draft AAuth-Budgets September 2026
person servers are different people. aud is the resource itself. In
three-party access ps equals iss. In four-party access iss is the
AS, which did not mint sub, so a key on iss would let two person
servers behind one AS collide; ps is the person server the AS
verified sent the token request ([I-D.hardt-oauth-aauth-protocol],
PS-to-AS Token Request). This document introduces no new identifier.
The ledger is not a second ceiling. A resource MUST NOT refuse a
request that fits its token's budget because a per-person total has
reached some figure the resource inferred; no party told it such a
figure, and the budgets it was handed are what it was authorized to
honor. Holding a person's spending across concurrent tokens within
bounds is the PS's job Section 14.7, because the PS is the party that
issues them and the only one that knows the ceiling Section 8.1.
A per-agent ceiling is not a resource-side key either. A person
server that wants one agent capped at less than another issues it a
smaller allocation Section 8.1; the enforcement is the token's own
budget, and no resource-side dimension is involved. What a resource
cannot supply from allocations alone is how much each agent actually
spent, since an allocation is a ceiling rather than a figure — that
is what the per-key query at the usage endpoint serves
Section 15.2.2.
The ledger's key is the person, not the agent and not the mission.
An auth token names no agent, and a person's spending at a resource
is theirs whichever agent incurred it; a per-agent key would also
reset every time the person changed agents. mission_s256 is optional
— a token may carry one or not — so a mission-keyed ledger has no
bucket for a mission-less token, and Section 18 requires mission-less
tokens for standing inference budgets. The person is the only key
present on every auth token. Missions are an attribution dimension
over that ledger Section 14.11, not the ledger itself.
14.6. The Billing Account
A resource that meters usually charges someone for it, and the party
it charges is an account in its own systems. Nothing in a budget
names that account. The aggregation key above is (ps, sub, aud), and
sub is directed per person server — it identifies a person at one PS
and carries no meaning at the resource beyond what the resource has
learned about it.
For most resources that is sufficient and no mechanism is needed.
The base protocol keys a person's relationship with a resource on the
person server and sub precisely so it survives a change of agent, and
a resource holding one account per person looks the account up from
(ps, sub). tenant names the person's organization and is not part of
Hardt Expires 1 April 2027 [Page 34]
Internet-Draft AAuth-Budgets September 2026
the identifier ([I-D.hardt-oauth-aauth-protocol], Organization
Identification). Consumption then meters against the account the
resource already had.
Beyond that, two different questions arise, and they compose rather
than substitute. The first is asked once per person; the second on
every authorization.
*Which person is this?* The first budgeted request carrying a sub the
resource has not seen is a question for the person, not the agent,
and both access modes answer it with an interaction the person
completes at the party that holds the account.
In three-party access the resource asks. It puts an interaction
claim in the resource token, and the person server chains the person
through the resource's own flow — signing in, creating an account,
connecting a payment method — before completing its own consent
([I-D.hardt-oauth-aauth-protocol], Resource-Initiated Interaction).
Because the resource issues the resource token, it decides when to
ask again: once per person, or once per mission, since it sees
mission_s256 at that moment.
In four-party access the AS asks, returning 202 with
requirement=interaction to the person server's token request
([I-D.hardt-oauth-aauth-protocol], PS-AS Trust Establishment). This
is the same one-time binding the AS already performs to establish
trust with a person server, answering a second question at the moment
it is already asking the person who they are.
Because sub is directed per person server, a person reaching the same
resource through two person servers presents two identifiers. The
binding interaction is what attaches both to one account, and a
resource that skips it sees two people and bills two ledgers.
*Which of their accounts?* Binding establishes who the person is. It
does not say which of several accounts an authorization is for, and a
person who holds more than one at the resource has to say. Account
Binding ([I-D.hardt-oauth-aauth-protocol], Account Binding) carries
the answer: an OPTIONAL account parameter on the authorization
endpoint request, named from the resource's own namespace, echoed as
the account claim of the resource token and copied into the auth
token.
This applies in both access modes; how account reaches the issuer,
and what each party does with it, is specified there and not restated
here. In four-party access it reaches the AS in the resource token,
so the binding tells the AS who the person is and account tells it
which of their accounts this authorization bills.
Hardt Expires 1 April 2027 [Page 35]
Internet-Draft AAuth-Budgets September 2026
A metered resource should ask for account where a person may hold
more than one, because a budget enforced against the wrong account is
charged to the wrong payer. A resource holding one balance per
person needs none of it: binding is the whole mechanism, and account
never appears in its tokens.
None of this is specific to budgets, and this document defines no new
mechanism for it. It is stated here because a metered resource meets
these cases on its first request and the rest of this document is
silent on them.
14.7. Concurrency
An agent may hold several concurrent auth tokens at the same resource
— the mission_s256 claim means concurrent missions produce concurrent
tokens, each with its own budget, for up to an hour. Handling this
is mandatory, not optional:
* A resource MUST apply the reserve-commit-release invariant of
Section 14.8 atomically per auth token, so that concurrent
requests presenting the same token cannot together exceed its
budget.
* A resource MUST post consumption to the (ps, sub, aud) ledger
Section 14.5 atomically, so that concurrent requests across
different tokens do not lose or double-count against the record
and counters.
* A PS SHOULD size per-token budgets so that their sum stays within
whatever standing ceiling it holds for the person at that
resource. This is the only place the cross-token total is
enforced; Section 14.4.1 is how an issuer keeps that sum exact as
allocations expire.
The bound on over-issuance is the auth token lifetime multiplied by
the number of concurrent tokens. A PS that issues _n_ concurrent
tokens of _X_ each has authorized up to _nX_ for as long as an hour,
regardless of any standing figure it intended to hold.
14.8. The Budget Is a Hard Cap
A resource MUST NOT let metered consumption exceed the granted
amount. A budget is an authorization, and an authorization the
enforcer may exceed is a hint.
Output is metered after it is generated, so honoring the cap means
bounding the request before serving it. Before serving a chargeable
request, the resource determines the request's maximum cost — from a
bound the request declares, such as a maximum output length, or from
a documented default — and refuses the request with
Hardt Expires 1 April 2027 [Page 36]
Internet-Draft AAuth-Budgets September 2026
reason=insufficient-budget Section 13.1 when that maximum exceeds the
remainder. An operation with no finite cost bound MUST be given one,
be truncated when the remainder is consumed, or be refused.
The implementation shape is reserve-commit-release: atomically
reserve the maximum against the grant, serve, commit the actual
charge, release the difference. The normative requirement is the
invariant, not the mechanism: committed consumption plus outstanding
reservations MUST NOT exceed the granted amount at any moment,
including under concurrent requests Section 14.7.
The honest cost of the invariant lands near exhaustion: a request
bounded at more than the remainder is refused even when its actual
cost would have fit. The mitigation is the agent's — read remaining
from the refusal's AAuth-Budget header and retry with a bound that
fits — and that is the correct pressure, since it rewards realistic
bounds.
14.9. Failed Calls
Whether a request that fails — a 5xx after input tokens were consumed
— draws down the budget is the resource's metering policy. Whatever
it meters, the record and counters MUST reflect, or the person's
numbers will not reconcile with the bill.
14.10. Delegation
A budget is not delegable. Each auth token carries its own budget,
and consumption through a delegated token draws down that token's
budget alone. Both delegation paths — call chaining and sub-agent
authorization ([I-D.hardt-oauth-aauth-protocol], Agent Delegation) —
obtain each downstream auth token through the person server, so the
PS sizes every grant in a chain, and Section 14.7 already governs
their sum. No sub-budget or draw-down-from-parent mechanism is
defined.
14.11. Mission Attribution
Consumption is attributed to a mission using the mission_s256 claim
of the auth token, beside its ps claim: a mission is identified by
the PS that approved it together with mission_s256
([I-D.hardt-oauth-aauth-protocol], Mission Identifier). mission_s256
is PS-asserted throughout: the person server validated the mission
when it issued the person token, the resource copied it into the
resource token, and the person server, and in four-party access the
AS, verified that it matches the presented token's
([I-D.hardt-oauth-aauth-protocol], Resource Token Verification). No
party in the chain takes the mission on the agent's word, which is
Hardt Expires 1 April 2027 [Page 37]
Internet-Draft AAuth-Budgets September 2026
what makes a resource's per-mission figures worth reading.
15. Usage Counters
The consumption record reaches the issuer only when the agent brings
a resource token back. The agent is the party being budgeted and
also the courier of the evidence: it cannot falsify the record, but
between re-authorizations it does not appear, and the issuer is blind
for up to an hour per token. The usage_endpoint Section 5 removes
the agent from that loop: the issuer — the PS, or in four-party
access the AS — queries the resource directly, on a channel the agent
is never on.
The endpoint serves *usage counters*: pre-summed consumption totals
the PS reads, acts on, and displays. It does not compute, convert,
or round. The resource keeps a handful of running integers,
incremented at metering time; serving the endpoint requires no per-
record history.
15.1. Usage Request
The caller — a person server, or in four-party access an access
server — MUST make a signed POST to the usage_endpoint, signing as a
server in its own right: an HTTP Sig under the jwks_uri scheme, with
id its issuer and dwk its metadata document
([I-D.hardt-oauth-aauth-protocol], Keying Material), and the
signature additionally covering content-type and content-digest, as
on a request with a body to a PS or AS
([I-D.hardt-oauth-aauth-protocol], Covered Components).
The body carries at most one *scope key*, naming a claim value the
resource has seen in auth tokens:
* *sub*: A directed person identifier. Scope: the person at this
resource, across all their agents and missions.
* *tenant*: A tenant identifier. Scope: the organization, across
its people.
* *mission_s256*: A mission identifier. Scope: one mission.
and one OPTIONAL member:
* *jkts*: An array of JWK Thumbprints ([RFC7638]), each naming a
signing key the resource has seen present an auth token. Asks for
what each of those keys consumed Section 15.2.2.
An access server names the person server whose values it is asking
about:
Hardt Expires 1 April 2027 [Page 38]
Internet-Draft AAuth-Budgets September 2026
* *ps*: The person server whose namespace the scope key belongs to.
REQUIRED when the caller is an access server and the request
carries a scope key, since the tokens an AS issues carry values
from every person server that federates with it. A person server
omits it; it is the caller.
A request MUST carry a scope key or jkts, and MAY carry both. At
most one scope key may appear. A request with more than one scope
key, or with neither a scope key nor jkts, is an error Section 15.3.
POST /usage HTTP/1.1
Host: inference.example
Content-Type: application/json
Content-Digest: sha-256=:...:
Signature-Input: sig=("@method" "@authority" "@path"
"content-type" "content-digest"
"signature-key");created=1754620000
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwks_uri;id="https://ps.example";
dwk="aauth-person.json";kid="key-1"
{
"sub": "8f14e45fceea167a5a36dedd4bea2543",
"jkts": ["NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs",
"0ZcOCORZNYy-DWpqq30BbmLzO1Yw3ZQhIgnHZQKxNVE"]
}
15.2. Usage Response
{
"as_of": 1754619970,
"aud": "https://ps.example",
"unit": "USD",
"decimals": 6,
"sub": "8f14e45fceea167a5a36dedd4bea2543",
"usage": {
"day": 1243180,
"week": 3118400,
"month": 8432650,
"year": 39847220,
"all_time": 61438050
},
"jkts": {
"NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs": 38215600,
"0ZcOCORZNYy-DWpqq30BbmLzO1Yw3ZQhIgnHZQKxNVE": 23222450
}
}
Hardt Expires 1 April 2027 [Page 39]
Internet-Draft AAuth-Budgets September 2026
* *as_of* (REQUIRED): The time through which the figures are
complete, in seconds since the Unix epoch. Metering aggregation
MAY lag serving; as_of is what keeps a lagging figure honest.
* *aud* (REQUIRED): The caller the response was produced for — a
person server, identified as in the ps claim of a resource token,
or an access server, identified as in the iss claim of an auth
token. It is what stops a signed response being presented to a
third party as a statement about them Section 15.2.4.
* *unit* (REQUIRED) and *decimals* (REQUIRED): The unit every figure
in the response is denominated in, and its scale, as in the budget
object Section 4.5.
* The *scope key* from the request, echoed unchanged — sub, tenant,
or mission_s256 — present only when the request carried one.
* *usage* (REQUIRED when the request carried a scope key): Calendar
counters for that scope.
* *jkts* (REQUIRED when the request carried jkts): An object mapping
each thumbprint to what that key consumed Section 15.2.2.
15.2.1. Calendar Counters
The members of usage are day, week, month, year, and all_time,
following the interval enumeration of Stripe Issuing [Stripe.Issuing]
Appendix B.4. Each is a non-negative integer giving consumption
within the current period:
* *day*: since 00:00 UTC today.
* *week*: since Monday 00:00 UTC of the current ISO 8601 week.
* *month*, *year*: since the start of the current UTC calendar month
or year.
* *all_time* (REQUIRED): everything the resource has metered against
budgets under this key.
All period boundaries are UTC. This is a definition, not a
deployment choice: no timezone appears in metadata or in the
response, and every party computes the same figure. The cost is that
"today" resets mid-afternoon in Auckland, which Stripe Issuing
accepts for the same reason this document does — the counter is
decision context, not a bill.
The calendar counters other than all_time are OPTIONAL; a resource
omits periods it does not track. For a mission_s256 query, all_time
is the mission total — the figure a PS wants when deciding whether to
fund a mission's continuation — and a resource MAY serve it alone.
Counters are subject to the 15-digit bound of Section 4.4. A
resource whose cumulative figure would exceed it omits that counter
rather than reporting an inexact number.
Hardt Expires 1 April 2027 [Page 40]
Internet-Draft AAuth-Budgets September 2026
all_time reaches as far back as the resource retains. This document
sets no retention requirement for scope keys; the person's bill is
the durable record.
15.2.2. Per-Key Figures
Each member of jkts is a thumbprint mapped to a single non-negative
integer: everything the resource has metered against budgets on auth
tokens presented by that key.
There are no calendar periods here. A key's consumption is already
bounded by the tokens issued to it, and an auth token lives at most
an hour; a key that has stopped presenting tokens has a figure that
no longer moves. Periods answer "how much this month", which is a
question about a person, not about a key.
A resource SHOULD retain a key's figure for at least 24 hours after
that key's last metered request, and MAY retain it longer. The bound
is idle time rather than age, so a key in continuous use is never
pruned. Beyond that window the PS is the party that accumulates: it
polls, it knows which keys belonged to which agent across rotations,
and it holds the history. The resource keeps a short tail.
A resource MUST omit a thumbprint from jkts rather than report zero
for it when it holds no figure — because the key is unrecognized, or
because its figure has been pruned. Absence means the resource
cannot answer; a present zero means the key consumed nothing. This
differs from the treatment of an unrecognized scope key Section 15.3,
and the reason is that there is nothing to conceal: the PS issued or
relayed every auth token, so it already knows the key exists, and a
zero that means "pruned" would be a wrong answer to an allocation
decision rather than a withheld one.
15.2.3. One Unit Per Response
Every figure in a response is in one unit, named once at the top
level, and it is the unit the resource meters in. There is no
request parameter selecting it.
The alternative is a per-unit array at every level, which costs every
response the shape needed by deployments that meter in one unit —
which is nearly all of them, since a resource that meters several
quantities collapses them to one billing unit before denominating a
budget Section 1.1. A resource may still declare several units in
budget_units Section 5, because that is what an agent may ask a
budget to be denominated in; what this endpoint reports is what the
resource actually metered, and the response says which unit that was.
Hardt Expires 1 April 2027 [Page 41]
Internet-Draft AAuth-Budgets September 2026
15.2.4. The Signed Response
Signing the usage response is RECOMMENDED. A resource that signs
uses an HTTP Sig with a key from the jwks_uri in its resource
metadata Section 5 — the same key material the PS already fetched to
verify resource tokens. The signature MUST cover @status, content-
type, and content-digest, and MUST be bound to the request by
covering the request's @authority and @path with the req parameter
([RFC9421]).
HTTP/1.1 200 OK
Content-Type: application/json
Content-Digest: sha-256=:...:
Signature-Input: sig=("@status" "content-type"
"content-digest" "@authority";req "@path";req);
created=1754620001
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwks_uri;id="https://inference.example";
dwk="aauth-resource.json";kid="r1"
The endpoint reports what a person owes for, so an unsigned figure is
one the party that produced it can later disown. Signing makes the
resource committed to what it reported: it cannot tell the person
server one number and the biller another. It does not make the meter
honest — the resource is the counterparty as well as the signer — and
Section 19.4 covers what remains.
It also closes an asymmetry. The consumption record Section 7.1 is
already resource-signed, because it rides inside a resource token.
The usage endpoint is the only issuer-facing consumption channel that
is not.
It is RECOMMENDED rather than REQUIRED because the figures are
decision context rather than authorization, and because this would be
the first response-side signature in the AAuth family — the signature
profile is request-side throughout Section 19.1. A person server
receiving an unsigned response is not in a position to do anything
but read it: refusing it leaves the PS with no figures rather than
unattributable ones, which is the worse of the two. What signing
changes is whether the resource can later disown what it said, and
that is worth having wherever both ends will implement it.
aud is what keeps the signed response non-transferable. Without it a
resource-signed statement of consumption could be handed to a third
party as though it described them, and the figures carry no other
indication of who asked.
Hardt Expires 1 April 2027 [Page 42]
Internet-Draft AAuth-Budgets September 2026
15.3. Authorization and Errors
The id parameter of the Signature-Key header is the caller's server
identifier, and its dwk names the metadata document that says which
role is calling — aauth-person.json for a person server, aauth-
access.json for an access server ([I-D.hardt-oauth-aauth-protocol],
Keying Material). That identifier is what the response carries as
aud Section 15.2. A caller is a person server or an access server.
The resource MUST only answer for values that have appeared in auth
tokens it accepted whose iss or ps claim names the caller: for a PS,
the tokens it issued in three-party access and the tokens carrying it
as ps in four-party access; for an AS, the tokens it issued, and for
a scope key only those whose ps is the request's ps. This applies to
thumbprints in jkts as much as to scope keys. sub is directed per PS,
so one person server cannot even name another's subjects; tenant,
mission_s256, and thumbprints are not directed, and this check is
what stops a third party from querying them.
An AS is entitled because it sizes allocations against a ceiling of
its own Section 4.2 and is bound by Section 14.4 for them. An AS
operated by the resource may take the same figures from the resource
directly; the endpoint is for the AS that is not.
A query for a scope key the resource does not recognize returns 200
with usage omitted; "never seen" and "nothing consumed" are
deliberately indistinguishable, so that a query cannot be used to
discover whether a person holds an account. Unrecognized thumbprints
are handled differently and for a stated reason Section 15.2.2.
invalid_request, using the error response format of
([I-D.hardt-oauth-aauth-protocol]), is returned for a body carrying
more than one scope key, carrying neither a scope key nor jkts,
carrying a scope key from an access server without ps, or carrying a
malformed value.
A resource MAY rate-limit the endpoint, using the RateLimit fields
([I-D.ietf-httpapi-ratelimit-headers]) as on any endpoint. A PS
SHOULD poll no faster than its decisions require.
15.4. Division of Labor
The two issuer-facing channels answer different questions at
different moments. The consumption record Section 7.1 serves the re-
authorization decision: it arrives in-band, resource-signed, at no
round-trip cost, exactly when the issuer is deciding. The usage
counters serve everything else: supervision between re-
authorizations, settlement of allocations that expired unreported
Section 14.4.1, mission totals, tenant-level exposure, how a person's
Hardt Expires 1 April 2027 [Page 43]
Internet-Draft AAuth-Budgets September 2026
spending divides among their agents Section 15.2.2, and the person's
dashboard. A metered resource SHOULD implement both; the narrowing
chain Section 4.2 functions with the record alone. The agent's own
view is neither of these: it is the AAuth-Budget header Section 11,
scoped to the token it holds and to the request it just made.
16. Capability Negotiation
This document adds budget to the AAuth Capability Value Registry. An
agent that understands budget semantics — the budget claim, the
AAuth-Budget header, and the reason values Section 13.1 — SHOULD
include budget in its AAuth-Capabilities request header, and in the
capabilities parameter of its person token and auth token requests
([I-D.hardt-oauth-aauth-protocol], AAuth-Capabilities Request
Header). The person token request is where a budget-aware agent
first reaches its PS about a resource, so it is where the declaration
first matters:
AAuth-Capabilities: interaction, clarification, budget
This tells the resource up front whether the agent will read what it
sends, rather than leaving it to rely on ignore-unknown-parameter
behavior. It is the same negotiation interaction performs for
requirement=interaction. Recipients MUST ignore unrecognized
capability values.
A resource MUST NOT withhold AAuth-Budget from an agent that did not
declare the capability. The header is safe to ignore.
17. Errors
Narrowing is never an error. A resource that will grant less than
the agent asked for issues a smaller budget; a PS or AS that will
allow less issues a smaller amount. No error is returned in either
case.
Errors are reserved for statements that cannot be reconciled:
Hardt Expires 1 April 2027 [Page 44]
Internet-Draft AAuth-Budgets September 2026
+=================+========+===============+========================+
| Error | Status | Endpoint | Meaning |
+=================+========+===============+========================+
| invalid_budget | 400 | Authorization | The budget object is |
| | | endpoint | malformed, or names |
| | | | a unit the resource |
| | | | has not declared in |
| | | | budget_units |
+-----------------+--------+---------------+------------------------+
| invalid_budget | 400 | PS and AS | The resource token's |
| | | auth token | budget.decimals |
| | | endpoints | disagrees with the |
| | | | value declared for |
| | | | that unit in the |
| | | | resource's |
| | | | budget_units |
| | | | metadata, or the |
| | | | object is otherwise |
| | | | malformed |
+-----------------+--------+---------------+------------------------+
| invalid_request | 400 | Usage | More than one scope |
| | | endpoint | key, neither a scope |
| | | | key nor jkts, or a |
| | | | malformed value |
| | | | Section 15.3 |
+-----------------+--------+---------------+------------------------+
Table 3
Error responses use the error response format defined in AAuth
Protocol ([I-D.hardt-oauth-aauth-protocol]).
invalid_budget at the authorization endpoint parallels invalid_scope:
the agent named something the resource does not recognize.
A decimals mismatch at the PS or AS MUST be a hard reject rather than
a narrowing. A mismatch means one of the two figures is stale, and
guessing which one is worse than failing — the error modes are a
budget interpreted a thousandfold too large or too small.
A PS or AS that has no cached budget_units for the resource and
cannot fetch the metadata MAY proceed on the resource token's
decimals, which is signed by the resource. The cross-check is a
defense against staleness, not against the resource.
Hardt Expires 1 April 2027 [Page 45]
Internet-Draft AAuth-Budgets September 2026
17.1. Declining with Guidance
A PS granting less than the resource offered needs no mechanism: the
amount in the issued claim says it. A PS or AS that declines a token
request outright on budget grounds MAY include *suggested_budget* in
its error response body: a budget object Section 4.5 whose unit and
decimals are copied from the resource token, and whose amount is what
the issuer would currently accept. It is guidance, not a grant — the
agent's move is a fresh resource token at that figure Section 6 and a
new token request, with the usual consent and policy evaluation.
In four-party access an AS's suggested_budget reaches the PS, not the
agent. The PS relays it in the problem+json body with which it
relays the AS's error to the agent ([I-D.hardt-oauth-aauth-protocol],
Auth Token Delivery), with unit and decimals unchanged and amount
lowered to what the PS would itself currently accept for the person
where that is lower. That figure never exceeds the ceiling the PS
holds Section 8.1, and the PS states it as an amount it would accept,
not as the ceiling, which the agent does not learn Appendix A.6.
18. Standing Authorization for Metered Inference
Metered inference is the initiating use case for this extension and
has a property that distinguishes it from most resource access: the
agent needs it before it can do anything else, including decide what
else it needs.
*Inference authorization is agent-scoped and standing.* It is
established at first PS contact, not per mission. The reason is a
termination argument rather than a preference: a per-mission
inference budget requires the agent to run inference in order to
evaluate whether it needs more inference budget, and that evaluation
itself consumes inference. A standing allocation terminates; a per-
mission one does not.
*Mission-less auth tokens are already legal.* mission_s256 is
optional in a person token and therefore in everything derived from
it, and the PS permission and interaction endpoints work with or
without a mission. No new token type is needed for this pattern.
*Do not model the harness as a mission.* A mission description is
defined as human intent expressed in Markdown. Synthesizing an
"operating mission" or "harness mission" to hold the inference budget
would put a fabricated description into the content-addressed blob
that every token carrying mission_s256 is bound to, and mission
revocation — a kill switch for one piece of work — would then also be
the kill switch for the agent's ability to think. Those are two
distinct controls and conflating them is a mistake.
Hardt Expires 1 April 2027 [Page 46]
Internet-Draft AAuth-Budgets September 2026
*Pre-mission inference runs on a mission-less auth token* and lands
in the person's usage with no mission attribution. Mission
boundaries become natural auth token re-issuance points, which costs
nothing: the agent is already talking to its PS to create the
mission.
*AP-bundled inference is out of scope.* Where the agent provider pays
for the agent's inference, the agent token is the credential, the
person server never appears, and nothing in this document applies.
This is the common deployment today, and readers will otherwise
assume the extension covers it. The case that does engage this
extension is a harness written by the agent provider spending against
the _person's_ inference account — which is where a ceiling matters
most, because the agent provider's software is deciding how much of
the person's money to consume.
*A TPX provider adds agent support without touching its meter.* A TPX
[TPX] provider already prices per token, reports cost in each
response's usage, and enforces a person-granted budget as a hard cap
— for human-driven apps holding OAuth grants. Serving agents means
accepting AAuth auth tokens carrying budget beside those grants: two
authorization envelopes over one metering core. Deployed this way,
this extension is the AAuth binding of TPX.
Neither envelope displaces the other. A person driving an app and an
agent acting for that person are different situations, and a provider
serving both has one meter under two front doors Section 22.
19. Security Considerations
19.1. The Header Is Unsigned
AAuth-Budget is not signed, so an intermediary can lie about the
balance. The failure modes are bounded. Understating remaining
makes the agent re-authorize earlier than it needed to. Overstating
it makes the agent hit an unexpected 401. Neither causes overspend,
because enforcement is the resource checking metered consumption
against the signed budget claim of the auth token — the header is a
pacing signal, not the authorization.
An implementation that needs the balance to be trustworthy rather
than merely harmless can cover AAuth-Budget with an HTTP Message
Signature on the response ([RFC9421]), as the usage endpoint
recommends for its own responses Section 15.2.4. It is not required
here, and the difference is what each response is for. A usage
response is a statement of what a person owes for, read by a party
that may later have to hold the resource to it. A balance on a
request response is a pacing signal the agent acts on immediately and
Hardt Expires 1 April 2027 [Page 47]
Internet-Draft AAuth-Budgets September 2026
re-reads on the next call, and signing every metered response to
protect a figure that is superseded seconds later buys little for
what it costs at inference volumes.
19.2. Budget Is Not a Substitute for Scope
A budget bounds how much an agent may consume, not what it may do.
An agent with a small budget at a resource that performs irreversible
actions can still perform them. Resources MUST continue to enforce
scope, and SHOULD NOT treat the presence of a budget as evidence that
the person reviewed anything beyond the amount.
19.3. Over-Issuance Through Concurrency
A PS that issues concurrent auth tokens without tracking their sum
has authorized their sum, not any single one of them Section 14.7. A
resource that aggregates non-atomically across concurrent requests
can be driven past the ceiling by parallel calls. Both are
implementation errors that produce real overspend, and both are easy
to make.
19.4. Consumption Reports as Attack Surface
The budget_consumed record is resource-signed and usage counters are
served from the resource's authenticated endpoint, signed where the
resource follows Section 15.2.4; both are the resource's own account
of what it metered. A resource that inflates them can induce a PS to
authorize more than the person intended, or to refuse further
authorization. An issuer SHOULD reconcile them against the person's
billing relationship with the resource where one exists, and SHOULD
NOT treat them as authoritative for anything other than its own next
decision.
Signing the usage response Section 15.2.4 does not change this. It
makes the resource committed to a figure rather than able to disown
it, which is what stops it reporting one number to the person server
and another to the biller. It does not make the meter honest,
because the resource meters, reports, and signs. The person's bill
is the record a dispute settles against, and a signed report is
evidence of what the resource said, not of what it consumed.
Hardt Expires 1 April 2027 [Page 48]
Internet-Draft AAuth-Budgets September 2026
19.5. Unit Substitution
Because a resource may change the unit between what the agent
requested and what it offers Section 4.2, a PS reading a budget claim
must read unit and decimals rather than assuming the denomination the
agent described in justification. The cross-check against
budget_units in Section 17 is the defense against a stale scale;
there is no defense against a resource that misdenominates
deliberately, and none is needed — that resource is equally free to
ignore the budget it was granted.
20. Privacy Considerations
A budget amount is financial information about the person. It
travels from the resource to the PS in the resource token and back in
the auth token, and appears in plaintext in the AAuth-Budget response
header on every response.
The consumption record and usage counters are more revealing than the
budget itself: counters describe the person's spending at that
resource over time and by mission, and the record itemizes one grant.
All of it is visible to the person's PS by design — the PS is
deciding on the person's behalf.
The two channels differ in who else can read them. Usage counters
travel only between resource and issuer, so tenant-scope and long-
horizon figures exist nowhere the agent can see. The consumption
record rides in the resource token, which the agent relays and can
read. It names only the token the agent itself presented, and only
as a jti and an amount — an agent learns nothing about the person's
other tokens or other agents from it — and a resource that considers
even that too revealing omits budget_consumed and serves the usage
endpoint alone.
Because AAuth-Budget is unsigned and unencrypted above TLS, every
intermediary on the path sees the person's remaining balance at that
resource, and the price of the request that produced the response.
Carrying no cumulative figure bounds this: an intermediary sees what
one call cost and what is left of one hour's grant, not the person's
spending history at that resource. Deployments that consider even
the per-call figure sensitive may omit the OPTIONAL cost member from
the header, at the price of leaving the agent to derive it from
successive remaining values.
21. IANA Considerations
Hardt Expires 1 April 2027 [Page 49]
Internet-Draft AAuth-Budgets September 2026
21.1. HTTP Header Field Registration
This specification registers the following HTTP header field in the
"Hypertext Transfer Protocol (HTTP) Field Name Registry" established
by [RFC9110]:
* Header Field Name: AAuth-Budget
* Status: permanent
* Structured Type: Dictionary
* Reference: This document, Section 11
21.2. JWT Claims Registration
This document requests registration of the following claims in the
IANA "JSON Web Token Claims" registry established by [RFC7519]:
+=================+====================+============+=============+
| Claim Name | Claim Description | Change | Reference |
| | | Controller | |
+=================+====================+============+=============+
| budget | Authorized | IETF | This |
| | spending ceiling | | document, |
| | at a resource | | Section 4.5 |
+-----------------+--------------------+------------+-------------+
| budget_consumed | What the presented | IETF | This |
| | auth token | | document, |
| | consumed, reported | | Section 7.1 |
| | by a resource | | |
+-----------------+--------------------+------------+-------------+
Table 4
21.3. AAuth Capability Value Registry
This document requests registration of the following value in the
AAuth Capability Value Registry established by AAuth Protocol
([I-D.hardt-oauth-aauth-protocol]). The registry policy is
Specification Required ([RFC8126], Section 4.6).
+========+===========================+
| Value | Reference |
+========+===========================+
| budget | This document, Section 16 |
+--------+---------------------------+
Table 5
Hardt Expires 1 April 2027 [Page 50]
Internet-Draft AAuth-Budgets September 2026
21.4. No Budget Unit Registry
This document deliberately establishes no registry of unit values.
Units are declared by each resource in its budget_units metadata,
exactly as scope values are declared in scope_descriptions. See
Appendix A.10.
22. Implementation Status
_Note: This section is to be removed before publishing as an RFC._
This section records the status of known implementations of the
protocol defined by this specification at the time of posting of this
Internet-Draft, and is based on a proposal described in [RFC7942].
The description of implementations in this section is intended to
assist the IETF in its decision processes in progressing drafts to
RFCs.
22.1. Implementations in Progress
*tokenpony* (Infinite Logic PBC) meters LLM inference and implements
TPX [TPX], an OAuth 2.0 profile carrying the same grant for human-
driven apps. That deployment is live, and this extension is being
added over the same metering core, with AuthGravity as the person
server and Harness News as the agent, in four-party access. TPX is a
complete deployment on its own: it needs no person server and nothing
from this document, and the two specifications share no wire surface
— one meter, two independent authorization envelopes.
*Regent Protocol* is in production at get4agent.com (marketplace
resource and provisioning server): allocations, refusals with
required, the streaming cost-omitted mode, the usage endpoint, and
sub-agent delegation. Its open-source resource/PS middleware regent-
httpsig (Python, Apache-2.0) publishes test vectors.
*The editor* is implementing this extension in several services.
Implementation reports and test vectors are expected from these
efforts and will be recorded here.
23. Document History
_Note: This section is to be removed before publishing as an RFC._
* draft-hardt-aauth-budgets-00
- Initial submission, written against draft-hardt-oauth-aauth-
protocol-11.
Hardt Expires 1 April 2027 [Page 51]
Internet-Draft AAuth-Budgets September 2026
24. Acknowledgments
The author thanks Abay Aubakirov, Alex Polvi, and Karl McGuinness for
feedback on early drafts.
25. References
25.1. Normative References
[I-D.hardt-httpbis-signature-key]
Hardt, D. and T. Meunier, "HTTP Signature Keys", 2026,
<https://datatracker.ietf.org/doc/draft-hardt-httpbis-
signature-key>.
[I-D.hardt-oauth-aauth-protocol]
Hardt, D., "AAuth Protocol", 2026,
<https://datatracker.ietf.org/doc/draft-hardt-oauth-aauth-
protocol>.
[RFC7519] Jones, M., Bradley, J., and N. Sakimura, "JSON Web Token
(JWT)", RFC 7519, DOI 10.17487/RFC7519, May 2015,
<https://www.rfc-editor.org/info/rfc7519>.
[RFC7638] Jones, M. and N. Sakimura, "JSON Web Key (JWK)
Thumbprint", RFC 7638, DOI 10.17487/RFC7638, September
2015, <https://www.rfc-editor.org/info/rfc7638>.
[RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for
Writing an IANA Considerations Section in RFCs", BCP 26,
RFC 8126, DOI 10.17487/RFC8126, June 2017,
<https://www.rfc-editor.org/info/rfc8126>.
[RFC9110] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
Ed., "HTTP Semantics", STD 97, RFC 9110,
DOI 10.17487/RFC9110, June 2022,
<https://www.rfc-editor.org/info/rfc9110>.
[RFC9111] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
Ed., "HTTP Caching", STD 98, RFC 9111,
DOI 10.17487/RFC9111, June 2022,
<https://www.rfc-editor.org/info/rfc9111>.
[RFC9421] Backman, A., Ed., Richer, J., Ed., and M. Sporny, "HTTP
Message Signatures", RFC 9421, DOI 10.17487/RFC9421,
February 2024, <https://www.rfc-editor.org/info/rfc9421>.
Hardt Expires 1 April 2027 [Page 52]
Internet-Draft AAuth-Budgets September 2026
[RFC9651] Nottingham, M. and P. Kamp, "Structured Field Values for
HTTP", RFC 9651, DOI 10.17487/RFC9651, September 2024,
<https://www.rfc-editor.org/info/rfc9651>.
25.2. Informative References
[AP2] Google, "Agent Payments Protocol (AP2)", 2025,
<https://ap2-protocol.org/>.
[ERC20] Vogelsteller, F. and V. Buterin, "EIP-20: Token Standard",
2015, <https://eips.ethereum.org/EIPS/eip-20>.
[I-D.hardt-aauth-r3]
Hardt, D., "AAuth Rich Resource Requests (R3)", 2026,
<https://datatracker.ietf.org/doc/draft-hardt-aauth-r3>.
[I-D.ietf-httpapi-ratelimit-headers]
Polli, R., Ruiz, A. M., and D. Miller, "RateLimit header
fields for HTTP", Work in Progress, Internet-Draft, draft-
ietf-httpapi-ratelimit-headers-11, 23 May 2026,
<https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-
ratelimit-headers-11>.
[ISO4217] International Organization for Standardization, "ISO
4217:2015 Codes for the representation of currencies",
2015, <https://www.iso.org/iso-4217-currency-codes.html>.
[OBIE.VRP] Open Banking Implementation Entity, "Variable Recurring
Payments Profile, Read/Write Data API", 2024,
<https://openbankinguk.github.io/read-write-api-
site3/v4.0/profiles/vrp-profile.html>.
[ODRL] W3C, "ODRL Vocabulary and Expression 2.2", 2018,
<https://www.w3.org/TR/odrl-vocab/>.
[RFC7942] Sheffer, Y. and A. Farrel, "Improving Awareness of Running
Code: The Implementation Status Section", BCP 205,
RFC 7942, DOI 10.17487/RFC7942, July 2016,
<https://www.rfc-editor.org/info/rfc7942>.
[RFC9396] Lodderstedt, T., Richer, J., and B. Campbell, "OAuth 2.0
Rich Authorization Requests", RFC 9396,
DOI 10.17487/RFC9396, May 2023,
<https://www.rfc-editor.org/info/rfc9396>.
[RFC9457] Nottingham, M., Wilde, E., and S. Dalal, "Problem Details
for HTTP APIs", RFC 9457, DOI 10.17487/RFC9457, July 2023,
<https://www.rfc-editor.org/info/rfc9457>.
Hardt Expires 1 April 2027 [Page 53]
Internet-Draft AAuth-Budgets September 2026
[Stripe.Issuing]
Stripe, "Stripe Issuing: Card spending controls", 2026,
<https://docs.stripe.com/api/cards/object#card_object-
spending_controls>.
[TPX] Infinite Logic PBC, "TPX: Token Pony Express — An OAuth
2.0 Profile for Metered LLM Inference Grants", 2026,
<https://tokenpony.dev/spec/>.
[W3C.PaymentRequest]
W3C, "Payment Request API", 2025,
<https://www.w3.org/TR/payment-request/>.
[x402] x402 Foundation, "x402: HTTP 402 Payment Protocol", 2025,
<https://docs.x402.org>.
Appendix A. Design Rationale
A.1. Why an Integer in a Declared Scale
*Why not a Structured Field Decimal.* [RFC9651], Section 3.3.2 caps a
Decimal at 12 integer digits and 3 fractional digits, with round-
half-to-even serialization. Inference is priced per million tokens,
so a single call can cost on the order of $0.000015, which serializes
to 0.000. The header would report zero consumption on every request
while the person receives a bill.
*Why not a binary float.* IEEE 754 cannot represent 0.1 exactly, and
a budget is a running sum, so the error accumulates rather than
cancelling. No monetary prior art surveyed in Appendix B uses a
float.
*Why not a decimal string,* which is what most monetary prior art
does use ([W3C.PaymentRequest], [x402]). An integer in a declared
scale is the only representation under which the Structured Field
header value and the JWT claim value are the same number. A decimal
string in the claim beside a Structured Field Decimal in the header
would disagree at the third decimal place, and every implementation
would have to define its own rounding to reconcile them.
A.2. Why the Scale Is Explicit
x402 [x402] omits a scale because the asset contract address _is_ the
unit and the scale comes from the contract, immutably. USD does not
work that way. ISO 4217 [ISO4217] fixes the minor unit for USD at 2,
and 2 decimal places cannot express a fifteen-microdollar call. The
resource is therefore choosing a scale that the unit identifier does
not determine, and a scale that is chosen has to travel with the
Hardt Expires 1 April 2027 [Page 54]
Internet-Draft AAuth-Budgets September 2026
amount.
A.3. Why decimals Is in the Signed Claim
decimals is declared in budget_units metadata and also carried in the
budget claim. The duplication is deliberate, and the reason is not
primarily an attack argument — the resource is the enforcer, and a
resource willing to reinterpret the scale is equally willing to
ignore the budget outright.
The reasons are versioning and self-description:
* *Versioning.* Auth tokens live up to an hour. A resource changing
its declared precision as an ordinary product decision — cents to
micro-dollars — would silently reinterpret every token in flight
by a factor of ten thousand.
* *Self-description.* The resource is not the only reader. The PS
ledgers against the number, the person's dashboard displays it, a
proxy logs it, a dispute cites it. Each would otherwise have to
resolve the number against mutable metadata fetched at some
unspecified later time.
A.4. Why One Record
An earlier revision carried up to twenty consumption records in the
resource token, one per recent grant, so that a PS re-deciding saw
the person's recent spend at the resource without a round trip.
Working through the issuer's ledger, the presented token's figure is
the only one that is news. Every prior token either came back
through the same path when it was retired, in which case its record
already arrived, or expired unreported, in which case the
conservative rule Section 14.4 holds until a usage reading settles it
Section 14.4.1 — and a reading settles every expired allocation at
once, without naming any of them. The prior records cost roughly a
kilobyte in the 401 header and let an agent read what was spent under
tokens it never held, including the person's other agents'. The one
thing the list uniquely offered — sibling-token spend at a checkpoint
— is what the per-key query at the usage endpoint answers, per agent
rather than per token, on a channel the agent is not on.
The jti stays. Concurrent tokens are the normal case for an agent
running several missions, and an issuer holding several live
allocations for one agent cannot tell from an amount alone which of
them a figure belongs to. The alternative — attributing by agent key
and falling back to the conservative rule when ambiguous — makes the
fallback the normal case for exactly the agents doing concurrent
work. One short claim keeps the figure attributable and idempotent
per token.
Hardt Expires 1 April 2027 [Page 55]
Internet-Draft AAuth-Budgets September 2026
A.5. Why the Agent Is Not Told Its Cumulative Consumption
An earlier revision of this document carried a consumed member in the
header, giving what had been consumed against the auth token to date,
and a balance_endpoint where the agent could read the same figure on
demand. Both are gone. The agent is told what a request cost and
what is left; cumulative consumption is reported to the person server
and not to the agent.
Four reasons.
The figure is redundant. granted is in the auth token the agent
signed the request with, and remaining is in the response, so
cumulative consumption is granted - remaining whenever nothing is
reserved. Carrying a member the recipient can already compute is
weight without information.
The word is already spoken for. A consumption record is a {jti,
consumed} pair Section 7.1 where consumed means the total metered
against one token's whole budget. A person server reads those
records and this header's semantics against each other. Having
consumed mean a per-request figure in one place and a per-token total
in the other is the kind of collision that survives review and then
costs an implementer a day.
Cumulative spend is the more revealing figure. It describes a
pattern rather than a transaction, and AAuth-Budget travels unsigned
past every intermediary on the path Section 20. What the agent
genuinely needs per response is the price of the call it just made
and whether it can afford another. Both are per-request facts, and
that is what the field now carries.
There is a fourth reason that applies across tokens rather than
within one. An agent holding its cumulative consumption over a
series of allocations can watch the series and infer the ceiling
behind it — how much the PS is willing to release, and how fast. The
ceiling is deliberately not disclosed Appendix A.6, and a per-token
figure that reconstructs it by subtraction discloses it anyway.
The balance_endpoint went with it. It was OPTIONAL, existed for one
case — an ambiguous failure, where the agent cannot say whether a
request was metered — and cost a resource an endpoint to implement
and this document a section to specify. That case is now answered by
a rule the agent applies locally Section 12.3: assume the maximum
until a later remaining says otherwise. A conservative default that
every agent applies is better than an optional round trip that some
resources offer.
Hardt Expires 1 April 2027 [Page 56]
Internet-Draft AAuth-Budgets September 2026
A.6. Why the Granted Budget Is Not the Person's Ceiling
A person server could authorize the whole of a person's intended
spend at a resource in one auth token and let the agent draw it down.
TPX [TPX] does the OAuth equivalent: the budget sits on a durable
grant, the app spends against it unsupervised, and the person hears
about it when the grant runs dry. This document does not, and the
difference is not a missing feature.
*The figure is not knowable when it would have to be fixed.* A
mission is approved before the work is done, and the work is what
determines the cost. A person asked at approval for a number is
guessing. Too low and the agent stops mid-task and the person is
interrupted anyway. Too high and the number is not a control,
because the agent will never reach it and nothing is checked before
it does. An allocation sized against what the work has actually cost
so far does not require the guess to be right.
*A ceiling the agent can read is a ceiling the agent plans against.*
An agent that knows it has been authorized for a pool treats the pool
as available. An agent that knows only its current allocation asks
when the allocation runs out, and asking is what puts the PS back in
the decision. The justification accompanying that request, and the
consumption records arriving with it, are the person server's
evidence — and neither exists if the agent never has to come back.
*The check-in is the point, not a cost of it.* Re-authorization is
where the PS reads what the last allocation bought Section 8.2,
compares it against the mission, and chooses among its six responses
Section 8.3 — including the two that are not a number at all: asking
the agent to account for the spend, and ending the work. A single
up-front grant has no such moment. It has one, at approval, when the
least is known.
This is why the ceiling appears nowhere on the wire. There is no
claim for it, it may not be shared with the agent, and cumulative
consumption that would reveal it by subtraction is withheld as well
Appendix A.5. What the resource enforces is the allocation in the
token in front of it. What the person authorized is a matter between
the person and their PS.
Hardt Expires 1 April 2027 [Page 57]
Internet-Draft AAuth-Budgets September 2026
A.7. Why a Trailer Only Adds a Member
AAuth-Budget may be sent as a trailer, carrying cost for a response
whose cost was unknown when the header was written Section 12. This
document rejected trailers in an earlier revision, on the reasoning
of the RateLimit work ([I-D.ietf-httpapi-ratelimit-headers]):
intermediaries drop them, and combining a header value with a trailer
value complicates clients.
The first objection carries less weight here. It is largely a
browser property — fetch does not surface trailers — and the traffic
this document governs is an agent calling a resource, which is
server-to-server and commonly HTTP/2. Where a trailer is dropped
anyway, rule 3 of Section 12.2 makes that harmless.
The second objection is the real one, and Section 12.2 answers it
structurally rather than by mandating client behavior. A recipient
may discard trailer fields or merge them into the header set, and a
sender can neither observe nor control which. Restating a member
across the two would therefore produce a value that depends on the
recipient's choice: AAuth-Budget is a Dictionary, duplicate keys
resolve last-wins ([RFC9651], Section 3.2), so merging yields the
trailer's value and discarding yields the header's. Adding a member
that the header did not carry has no such fork. Merge yields a
complete picture, discard yields a conservative one, and neither is
wrong.
This is why reserved is a statement about a request rather than a
running balance. A running figure would go stale the moment the
reservation was released, and a stale member surviving a merge is
exactly the failure the rule exists to prevent. What was held for a
request does not change after the fact.
A.8. Why Not RateLimit
RateLimit and RateLimit-Policy ([I-D.ietf-httpapi-ratelimit-headers])
already report a server-side quota and its remaining balance.
Reusing them would avoid a new header. Four things prevent it, all
following from a budget being an authorization rather than a capacity
hint:
1. *Stated non-goal.* The RateLimit specification excludes
authorization from its scope. Reporting the balance of a PS-
issued grant through a field whose own specification says it is
not for access control is a misuse a reviewer will name.
Hardt Expires 1 April 2027 [Page 58]
Internet-Draft AAuth-Budgets September 2026
2. *No unit carrier.* q and r MUST be non-negative Integers, and the
quota units registry covers request, content-bytes, and
concurrent-requests. There is no currency carrier, so the
denomination would be invisible in the field reporting the
number.
3. *Opposite reliability contracts.* RateLimit says servers need not
send the fields on every response, clients must not assume future
responses will carry them, and a positive r is not a guarantee of
anything. Those are correct properties for a capacity hint and
wrong ones for the remaining portion of an authorization, which
is why Section 11.1 requires the field on every response the
metering layer answers rather than leaving it optional.
4. *Intermediary rewriting.* Intermediaries MAY tighten RateLimit
values. An intermediary tightening a budget balance is forging
authorization state.
The two fields are complementary and MAY appear on the same response.
A resource limiting an agent to 100 requests per minute _and_ to five
dollars of spend is stating two different things, and collapsing them
loses one.
A.9. Why Budget Is Not a Mission Aggregate
A mission description may well say "budget around $5,000," and the
obvious next step is to make that a protocol object the PS enforces
across every resource the mission touches. This document does not,
for two reasons.
The resources in a mission meter in units that do not add up. An
airline meters in USD, an inference endpoint in micro-dollars or
tokens, a storage service in gigabyte-months. Aggregating requires
conversion rates and a common denomination, neither of which the
protocol has, and both of which change.
More fundamentally, the enforcement point is wrong. The party that
can enforce a per-resource ceiling is the resource, because it
meters. No party meters the mission. A mission-level total is a PS
policy input — the PS decides how much of the person's $5,000 to
authorize at each resource as it goes — which is exactly what the
narrowing chain in Section 4.2 gives it. The mission description
already carries the person's intent, and the PS already reads it.
A.10. Why Units Are Not Registered
RateLimit establishes an IANA registry of quota units because its
units — request, content-bytes, concurrent-requests — are protocol-
generic and every server means the same thing by them.
Hardt Expires 1 April 2027 [Page 59]
Internet-Draft AAuth-Budgets September 2026
Budget units are not generic. A unit is meaningful only against a
resource's own pricing, and the parties that need to interpret it are
the resource that declared it and the PS that fetched the resource's
metadata. This is the same situation as scope values, which the base
protocol leaves to each resource's scope_descriptions rather than
registering. Registering USD would add nothing that ISO 4217 does
not already provide, and registering tokens would suggest an
interoperable meaning that does not exist.
No registry does not mean no constraint. A monetary unit SHOULD be
an ISO 4217 alphabetic code Section 4.5, which is what keeps a
consent screen able to render "$5.00" rather than a resource-invented
string the person has to interpret. What is left unregistered is the
non-monetary case, where no external register exists to point at.
Appendix B. Prior Art
This section is non-normative. It records where the field shapes and
value encodings in this document come from, and what gap remains.
B.1. RFC 9396, Rich Authorization Requests
[RFC9396] (OAuth WG, May 2023), Section 2.2 defines the complete set
of common data fields for an authorization detail: type, locations,
actions, datatypes, identifier, privileges. There is no amount or
quantity field among them. instructedAmount appears only in the
document's examples and belongs to the payment_initiation
authorization details type, which comes from Berlin Group NextGenPSD2
and carries ISO 20022 ActiveCurrencyAndAmount semantics; it is not
defined by RFC 9396.
Section 10 states that registration of authorization details types
with the AS is outside the specification's scope, and Section 14
registers the request parameter, the claim, the metadata fields, and
the invalid_authorization_details error — but establishes no registry
of types.
Section 6.1 is the load-bearing citation: there is no standardized
mechanism for comparing two arbitrary authorization detail requests,
and an AS should not rely on simple object comparison. AAuth Budgets
closes that gap for one narrow case. Because the unit is resource-
declared and the value is an integer in a declared scale, "is this
grant less than the prior one" is a numeric comparison rather than a
structural one.
R3 ([I-D.hardt-aauth-r3]) covers why AAuth does not profile RAR
generally.
Hardt Expires 1 April 2027 [Page 60]
Internet-Draft AAuth-Budgets September 2026
B.2. TPX
TPX [TPX] is an OAuth 2.0 profile for metered LLM inference grants:
apps ship without provider keys, and the person grants each app a
metered budget from a provider the person chooses and pays. The
budget rides RFC 9396 authorization_details as {type: "llm-
inference", budget, models}; the user or provider MAY grant less than
requested and the client MUST read the granted figure from the token
response — the requested/granted split of the narrowing chain
Section 4.2, in OAuth form.
Three of its mechanisms have direct counterparts here. Its GET
/credits spend summary and budget_used introspection member report
cumulative spend on a grant, which this document reports to the
person server Section 15 rather than to the agent Appendix A.5. Its
separation of budget_exhausted from balance_exhausted is the same
boundary Section 13.4 draws between re-authorization and payment.
And its budget is "a damage cap, not a payment" — the hard-cap
property Section 14.8 states, which TPX asserts as a property of the
grant and this document additionally realizes with reservations,
since an agent's operations are unbounded before generation.
TPX denominates in USD as decimal JSON numbers with at most six
fractional digits, where this document uses an integer in a declared
scale Appendix A.1. TPX serves human-driven apps over OAuth;
Section 18 describes the deployment where one provider's meter serves
both envelopes.
B.3. UK Open Banking Variable Recurring Payments
[OBIE.VRP] ControlParameters is the most complete standing-budget
model in production: MaximumIndividualAmount,
MaximumCumulativeAmount, MaximumCumulativeNumberOfPayments, and
PeriodicLimits[] carrying PeriodType and PeriodAlignment.
PeriodAlignment — Consent versus Calendar — names the choice the
usage counters Section 15 make: calendar alignment, with the timezone
question answered by fixing UTC.
B.4. Stripe Issuing Spending Controls
[Stripe.Issuing] spending_controls.spending_limits[] is {amount,
interval, categories}, with amount an integer in the currency's
smallest unit. interval spans per_authorization through all_time,
which is the precedent for expressing a per-transaction cap and a
periodic cap in a single enumeration rather than two fields; the
usage counters Section 15 adopt the date-based portion of that
enumeration directly. Stripe computes all date-based intervals from
midnight UTC — the precedent for fixing UTC by definition — and
Hardt Expires 1 April 2027 [Page 61]
Internet-Draft AAuth-Budgets September 2026
documents spending aggregation as best-effort with up to 30 seconds
of delay, the precedent for as_of.
B.5. W3C Payment Request API
[W3C.PaymentRequest] PaymentCurrencyAmount is {currency, value} with
value a decimal string. It is reused by Google's Agent Payments
Protocol [AP2] in CartMandate. AP2's IntentMandate carries a maximum
price, an expiry, and a merchant allowlist as user-signed
constraints, which is the closest prior art to a user-authorized
agent ceiling.
B.6. x402
[x402] carries maxAmountRequired as a string in the asset's atomic
units, alongside asset (a contract address) and network; v2 renames
the field to amount and moves network to CAIP-2 form. There is no
currency field: the asset identifier is the unit, and the exponent
comes from the contract. This is the precedent for using a unit
identifier rather than a currency field, and for decimals as the name
of the scale.
B.7. ODRL
[ODRL] payAmount with unit is the precedent for a field literally
named unit holding a currency code.
B.8. RateLimit Header Fields
[I-D.ietf-httpapi-ratelimit-headers] (HTTPAPI WG, Standards Track,
not yet an RFC) defines RateLimit-Policy with q, qu, w, and pk, and
RateLimit with r, t, and pk, both as RFC 9651 Lists. It establishes
an IANA RateLimit Quota Units registry (Specification Required) with
initial entries request, content-bytes, and concurrent-requests, and
three RFC 9457 problem types: quota-exceeded (429), temporary-
reduced-capacity (503), and abnormal-usage-detected (429).
It is cited here for why AAuth-Budget exists separately Appendix A.8,
for the trailer rules Section 12.2, and for pk as the precedent for a
documented, client-predictable partition key.
Author's Address
Dick Hardt
Hellō
Email: dick.hardt@gmail.com
Hardt Expires 1 April 2027 [Page 62]