| Internet-Draft | AAuth-Budgets | August 2026 |
| Hardt | Expires 21 February 2027 | [Page] |
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 recent consumption, 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.¶
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.¶
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."¶
This Internet-Draft will expire on 21 February 2027.¶
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.¶
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.¶
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.7.¶
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.5.¶
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.¶
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.¶
402 Payment Required and the resource's commercial arrangement with the person are untouched.¶
aa-resource+jwt, aa-auth+jwt), fields in aauth-resource.json, two resource endpoints, 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.¶
{::boilerplate bcp14-tagged}¶
decimals. An amount of 5000000 with decimals of 6 is 5.000000 of the unit.¶
budget claim of an auth token. The figure the resource enforces against.¶
{jti, consumed} pair reporting what the resource metered against one prior auth token's budget. Carried in the budget_consumed claim of a resource token.¶
usage_endpoint.¶
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:¶
| Position |
scope
|
budget
|
|---|---|---|
| Authorization endpoint request | what the agent asks for | what the agent asks for |
| Resource token | what the resource will grant | what the resource will grant |
| PS-to-AS token request | (n/a) | what the PS will allow |
| Auth token | granted, MUST NOT be broader | granted, MUST NOT exceed |
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.¶
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.¶
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."}¶
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.¶
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.¶
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.¶
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.¶
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]), PS Token Endpoint).¶
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.¶
Two carriers bound the representable range:¶
remaining, cost, and reserved in the AAuth-Budget header Section 11.¶
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.¶
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.¶
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 }
}
¶
This document extends the /.well-known/aauth-resource.json document defined in AAuth Protocol ([I-D.hardt-oauth-aauth-protocol]) with two fields:¶
{
"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"
}
¶
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 queries usage counters Section 15. The URL MUST conform to the Endpoint URL requirements of ([I-D.hardt-oauth-aauth-protocol]).¶
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.¶
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): An array of consumption records — what recent grants were actually consumed, as decision context for the PS. 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": "pt-3ab910",
"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 },
{ "jti": "at-5e22c1", "consumed": 431200 },
{ "jti": "at-08aa37", "consumed": 1250000 }
],
"iat": 1754611200,
"exp": 1754611500
}
¶
budget_consumed is an array of consumption records, most recent first. Each record has two members, both REQUIRED:¶
jti: The jti claim of a prior auth token.¶
consumed: What the resource metered against that token's budget, 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 any record it cannot state in that budget's unit and decimals — a record carried in a stale scale is the thousandfold error Section 17 in miniature.¶
Records cover prior auth tokens matching this token's context: the same (iss, sub, aud) the resource aggregates against Section 14.4, and, when this resource token carries mission_s256, the same mission_s256. A mission-context re-authorization thus reports that mission's grants; a mission-less one reports the person's most recent grants at this resource.¶
A resource SHOULD NOT include more than 20 records. The practical bound is size: the resource token travels inside the AAuth-Requirement header on the 401 path, where header-field limits apply; twenty records cost roughly a kilobyte after base64 encoding.¶
A record is deliberately two members and no more. Every jti it names is a token the PS issued — or, in four-party access, relayed from the AS — so the PS already holds the granted amount, the mission, the scope, and the issuance time for each record and joins them from its own ledger. Carrying those values again would duplicate what the PS knows and put more of the person's financial detail into a token the agent also reads. What the PS cannot know, and what the record supplies, is what the resource actually metered.¶
Records are attributable only when the resource can identify the person, which it can once it has verified an auth token for them. A resource issuing a resource token to an agent it has not previously authorized has nothing to report and omits budget_consumed.¶
The records ride 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. Totals over horizons longer than 20 grants are served at the usage endpoint Section 15.¶
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 PS 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.¶
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 consumption records 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 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.¶
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 last several grants actually cost, resource-signed, arriving at no round-trip cost.¶
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.¶
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.¶
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.¶
| 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 figure | Error response carrying suggested_budget Section 17.1
|
| End the work | Terminate the mission |
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
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
}
¶
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, or a DELETE withdrawing the request. The PS SHOULD enforce the base protocol's limit on clarification rounds. 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.¶
This document extends the PS-to-AS token request (([I-D.hardt-oauth-aauth-protocol]), AS Token Endpoint) with one parameter, used in four-party access only.¶
budget (OPTIONAL): A budget object Section 4.5 carrying the ceiling the PS will allow.¶
POST /token HTTP/1.1
Host: as.inference.example
Content-Type: application/json
Signature-Input: sig=("@method" "@authority"
"@path" "signature-key");created=1754611200
Signature: sig=:...signature bytes...:
Signature-Key: sig=jwks_uri;
jwks_uri="https://ps.example/.well-known/jwks.json"
{
"resource_token": "eyJhbGc...",
"agent_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 resource token carries budget and the PS omits this parameter, the AS MUST treat the resource token's budget as the PS's ceiling.¶
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" } },
"scope": "inference.completions",
"budget": { "amount": 2000000, "unit": "USD", "decimals": 6 },
"iat": 1754611200,
"exp": 1754614800
}
¶
Issuer rules:¶
unit and decimals from the resource token's budget claim unchanged.¶
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.¶
budget in an auth token when the resource token carried no budget claim. The resource, not the PS or AS, denominates.¶
Resource rules:¶
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.¶
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.¶
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.7. It is a floor — committed consumption will not exceed the grant — though the figure may lag metering. Exhaustion is signaled by the 401 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.7. 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.¶
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 a PS-facing figure, reported in the resource token Section 7.1 and at the usage endpoint Section 15; see Appendix A.4 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.¶
A resource that granted a budget SHOULD include AAuth-Budget on every response to a request bearing that auth token — success, error, and the 401 challenge, 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.7.¶
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.¶
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.¶
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.7, 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.¶
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:¶
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.¶
A resource MAY send AAuth-Budget as a trailer field, subject to three rules:¶
AAuth-Budget in a Trailer header field ([RFC9110], Section 6.6.1).¶
cost and nothing else.¶
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.¶
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.¶
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.7. 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.¶
Auth token expired. The budget expires with the token. This is already-defined behavior: 401 with AAuth-Requirement: requirement=auth-token; resource-token="...". This document adds nothing.¶
Budget exhausted, token still valid. The same response. The base protocol already permits a resource to return requirement=auth-token with a new resource token to a request that already carries an auth token, when the request requires higher authorization than the current token provides, and requires agents to be prepared for step-up at any time. Budget exhaustion is that case, and the agent's action is identical either way: take the fresh resource token to its PS.¶
Request exceeds the remainder. The budget has remainder, but this request's maximum cost exceeds it Section 14.7. 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 records and counters. The resource declined to serve it; metering the refusal would make exhaustion self-perpetuating.¶
The fresh resource token SHOULD carry updated budget_consumed records Section 7.1, which show the PS what the last several grants were actually consumed — the context for deciding whether to authorize more.¶
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: cost=180000; remaining=0;
unit="USD"; decimals=6
¶
reason is a Token. This document defines two values:¶
budget-exhausted: The granted budget is spent.¶
insufficient-budget: The budget has remainder, but this request's maximum cost exceeds it Section 14.7.¶
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.¶
A resource refusing under insufficient-budget has computed the request's maximum cost — Section 14.7 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. A new value would hard-fail every budget-unaware agent on a condition that 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.¶
429 Too Many Requests is not used. It is rate limiting, and in this protocol it already means "increase the polling interval by 5 seconds" in the deferred response state machine.¶
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.¶
The PS authorizes a number. The resource counts. The PS is not in the request path and does not meter.¶
A budget is carried in the budget claim of an auth token, so a resource can enforce one only where it issues 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 declares access_mode: auth-token for the endpoints it meters. It MAY continue to serve unmetered endpoints on a person token; access modes apply per endpoint. 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.¶
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. Any AAuth server that issues tokens MAY provide a revocation endpoint, and revoking an auth token by (iss, jti) (([I-D.hardt-oauth-aauth-protocol]), Token Revocation) ends its budget along with the rest of its authorization. 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.¶
Two conditions return the agent to the PS, and either is sufficient. The auth token expires, which the base protocol caps at one hour. 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.¶
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.7 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 (iss, sub, aud) of the auth token, which is what the consumption records Section 7.1 and the usage counters Section 15 report. (iss, sub) identifies the person — sub is unique within its issuer, and values from different issuers are different people — and aud is the resource itself. 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.6, 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.10, not the ledger itself.¶
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 (iss, 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 (iss, sub) precisely so it survives a change of agent, and a resource holding one account per person looks the account up from that pair, or from (iss, tenant, sub) where the person belongs to an organization. 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]), Access Server Federation). 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.¶
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.¶
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:¶
(iss, sub, aud) ledger Section 14.4 atomically, so that concurrent requests across different tokens do not lose or double-count against the records and counters.¶
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.¶
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 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.6.¶
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.¶
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 records and counters MUST reflect, or the person's numbers will not reconcile with the bill.¶
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]) — obtain each downstream auth token from the person server, so the PS sizes every grant in a chain, and Section 14.6 already governs their sum. No sub-budget or draw-down-from-parent mechanism is defined.¶
Consumption is attributed to a mission using the mission_s256 claim of the auth token. That claim 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 bound the resource token to the person token it had issued. No party in the chain takes the mission on the agent's word, which is what makes a resource's per-mission figures worth reading.¶
Consumption records reach the PS 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 records, but between re-authorizations it does not appear, and the PS is blind for up to an hour per token. The usage_endpoint Section 5 removes the agent from that loop: the PS 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.¶
The PS MUST make a signed POST to the usage_endpoint, authenticating exactly as it does at an AS auth_token_endpoint ([I-D.hardt-oauth-aauth-protocol]): an HTTP Sig whose Signature-Key header carries scheme=jwks_uri, with the signature additionally covering content-type and content-digest.¶
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.¶
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;
jwks_uri="https://ps.example/.well-known/jwks.json"
{
"sub": "8f14e45fceea167a5a36dedd4bea2543",
"jkts": ["NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs",
"0ZcOCORZNYy-DWpqq30BbmLzO1Yw3ZQhIgnHZQKxNVE"]
}
¶
{
"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
}
}
¶
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 person server the response was produced for, identified as in the ps claim of a resource 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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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;
jwks_uri="https://inference.example/.well-known/jwks.json"
¶
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. Consumption records Section 7.1 are already resource-signed, because they ride inside a resource token. The usage endpoint is the only PS-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.¶
The two PS-facing channels answer different questions at different moments. The consumption records Section 7.1 serve the re-authorization decision: they arrive in-band, resource-signed, at no round-trip cost, exactly when the PS is deciding. The usage counters serve everything else: supervision between re-authorizations, mission totals past the 20-record window, tenant-level exposure, how a person's 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 records 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.¶
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 PS token requests:¶
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.¶
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:¶
| Error | Status | Endpoint | Meaning |
|---|---|---|---|
invalid_budget
|
400 | Authorization endpoint | The budget object is malformed, or names a unit the resource has not declared in budget_units
|
invalid_budget
|
400 | PS and AS auth token endpoints | The resource token's budget.decimals 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 endpoint | More than one scope key, neither a scope key nor jkts, or a malformed value Section 15.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.¶
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.¶
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 a signed, content-addressed blob, 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.¶
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.¶
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 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.¶
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.¶
A PS that issues concurrent auth tokens without tracking their sum has authorized their sum, not any single one of them Section 14.6. 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.¶
budget_consumed records are 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. A PS 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.¶
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.¶
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.¶
Consumption records 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 records itemize it per 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 PS, so tenant-scope and long-horizon figures exist nowhere the agent can see. Consumption records ride in the resource token, which the agent relays and can read, and because records are matched per person rather than per agent Section 7.1, they can name tokens the person's other agents incurred. Each record is only a jti and an amount — the minimal record shape is what caps the exposure there — 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.¶
This specification registers the following HTTP header field in the "Hypertext Transfer Protocol (HTTP) Field Name Registry" established by [RFC9110]:¶
AAuth-Budget¶
This document requests registration of the following claims in the IANA "JSON Web Token Claims" registry established by [RFC7519]:¶
| Claim Name | Claim Description | Change Controller | Reference |
|---|---|---|---|
budget
|
Authorized spending ceiling at a resource | IETF | This document, Section 4.5 |
budget_consumed
|
Recent per-token consumption records reported by a resource | IETF | This document, Section 7.1 |
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 |
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.9.¶
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.¶
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 implementing both sides: the budget claim in auth tokens issued by its gate, with allocation and lifetime derived from the owner's mandate, and resource-side metering middleware in regent-httpsig performing atomic reserve-commit-release and emitting AAuth-Budget.¶
The editor is implementing this extension in several services.¶
Implementation reports and test vectors are expected from these efforts and will be recorded here.¶
Note: This section is to be removed before publishing as an RFC.¶
This document has not been submitted to the datatracker. Everything below is a change to the editor's copy, made while the design was being explored against implementations in progress. The log is reset at first submission, which becomes draft-hardt-aauth-budgets-00; readers wanting the detail behind any entry will find it in the repository's history and pull requests.¶
unit request parameter from the usage endpoint. It existed for a resource metering one person in more than one unit, which Section 1.1 already discourages, and it bought an error condition and an arbitrary notion of a primary unit. The response reports in the unit the resource meters in and says which that is.¶
requirement=clarification as the response for an escalation the PS is not ready to refuse or approve — the channel that lets a PS tell an agent it is overspending, which narrowing an amount cannot do.¶
cost entirely Section 12.1. The member previously had two carriers, header and trailer, and a resource that meters a streamed response on a runtime with no trailer support could use neither — making every such response non-conformant. reserved becomes REQUIRED in that case, and the agent recovers the exact figure from the following response's remaining. Removed the sentence in Section 12.3 stating that a resource emitting usage in its stream is not excused from the trailer.¶
required member of AAuth-Budget Section 13.2, the maximum cost a resource computed for a request it refused under reason=insufficient-budget. The figure was previously available only in the enclosed resource token, whose aud is the PS, which left the agent unable to size the shrink-and-retry that Section 13 offers it. It is deliberately distinct from the resource token's budget, which is a re-authorization offer rather than a fact about the refused request.¶
budget; the (iss, sub, aud) aggregate is a ledger for the records and counters and is not a second ceiling the resource may refuse against. Concurrency Section 14.6 said "aggregate atomically across all live auth tokens", which read as a cross-token cap; the atomicity requirements are now stated separately for the per-token invariant and for the ledger. Holding the cross-token total is the PS's job. Also stated why the ledger is keyed on the person: mission_s256 is optional and Section 18 requires mission-less tokens, so a mission-keyed ledger has no bucket for them.¶
unit and decimals are stated once at the top of the response rather than repeated per entry, since budget_units fixes the scale for a unit and a response reports in one unit Section 15.2.3; usage is consequently a counter object rather than an array. The response echoes the scope key and carries aud naming the person server it was produced for. The request takes at most one scope key, and both the scope key and unit are now optional given the addition below.¶
jkts query Section 15.2.2: the PS names the signing keys it wants figures for and the resource returns what each consumed. Allocations are ceilings, so a PS supervising several agents for one person could not previously learn how the spending divided among them without waiting for consumption records. Per-key figures carry no calendar periods — a key's spend is already bounded by tokens that live an hour — and a resource SHOULD retain one for 24 hours after that key's last metered request, leaving accumulation to the PS. An unrecognized or pruned key is omitted rather than reported as zero, which differs from the treatment of scope keys for a reason stated in place.¶
cost by subtraction is exact only for serial requests on one token; concurrent requests on one token recover the net of everything that settled between the two responses.¶
unit/decimals sentence in Section 11.¶
Added The Billing Account Section 14.5: which account a metered resource charges. Most resources need nothing beyond the (iss, sub) lookup the base protocol already provides. Beyond that there are two questions, asked at different frequencies and answered by different mechanisms: which person this is, answered once by an interaction the person completes at the resource (three-party) or at the AS (four-party); and which of their accounts an authorization is for, answered on every authorization by the account parameter and claim. No new mechanism; the document was silent on a question every metered resource meets on its first request.¶
Replaced the header's cumulative consumed member with cost, what this request cost. The agent's question per response is the price of the call it just made and whether it can afford another; cumulative spend answered neither, was derivable as granted - remaining, and collided with the consumed of a consumption record, which is a per-token total. See Appendix A.4.¶
Added the OPTIONAL reserved member, what the resource holds for a request whose cost it cannot yet state. It is a fact about the request and is never revised.¶
Permitted AAuth-Budget as a trailer, carrying cost for a streamed response. A trailer MUST NOT restate a member the header carried, which makes the field correct whether a recipient merges trailers or discards them, without mandating either. Reverses this document's earlier position that trailers are not used; see Appendix A.6.¶
Added Ambiguous Failures Section 12.3: an agent that never receives a response assumes the request cost what the resource had held for it, until a later remaining supersedes that.¶
Removed the balance_endpoint and its metadata field. It was OPTIONAL and existed for the ambiguous-failure case, which a local conservative rule now covers without a round trip.¶
Note: written against draft-hardt-oauth-aauth-protocol-11, which introduces the person token, replaces the mission object with mission_s256, and removes the agent identifier from auth tokens.¶
The author thanks Abay Aubakirov, Alex Polvi, and Karl McGuinness for feedback on early drafts.¶
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.¶
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 amount.¶
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:¶
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.5, 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.¶
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.4. 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.¶
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.¶
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:¶
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.¶
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 says MUST send rather than MAY.¶
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 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.¶
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.¶
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.¶
This section is non-normative. It records where the field shapes and value encodings in this document come from, and what gap remains.¶
[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.¶
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.4. Its separation of budget_exhausted from balance_exhausted is the same boundary Section 13.3 draws between re-authorization and payment. And its budget is "a damage cap, not a payment" — the hard-cap property Section 14.7 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.¶
[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.¶
[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 documents spending aggregation as best-effort with up to 30 seconds of delay, the precedent for as_of.¶
[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.¶
[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.¶
[ODRL] payAmount with unit is the precedent for a field literally named unit holding a currency code.¶
[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.7, for the rejection of trailers Section 12, and for pk as the precedent for a documented, client-predictable partition key.¶