DelegusDocsv0.3 rc1

Docs/Budgets

Guides

Budgets

A permission can carry a budget: a limit that gets used up. It can cap the total an agent may spend over the life of the permission, the amount it may spend in each day, week or month, or the number of times the permission may be used at all. Each allowed action spends from the budget; a refused action never does.

Release candidate. This describes v0.3-rc1, the current release candidate. New and existing relying parties are on it (profile delegus-base-v3). It is not frozen: the commitment construction is awaiting an external cryptography review, and the v0.2 specification stays the normative text until v0.3 is frozen. Details can still change. Budgets apply only to a relying party on delegus-base-v3; delegus-base-v1 is unchanged.

What a budget adds#

Without a budget, every check is about one action at a time: is this purchase under maxAmount, in the right currency, for an allowed resource. A budget adds memory. Delegus keeps a running count per permission, a consumption ledger, and checks each new action against what has already been spent.

Words used on this page:

The budget constraint#

A budget is one more key inside a Capability's constraints. It is an object with at least one of three members and no others:

MemberTypeMeaningAllowed on
totalnon-negative integer, minor units of the Capability's currencyThe most that may be spent over the Grant's whole lifecommerce:purchase only
perPeriod{ "amount": <non-negative integer, minor units>, "period": "day" \| "week" \| "month" }, exactly those two membersThe most that may be spent in one periodcommerce:purchase only
usespositive integerHow many ALLOW decisions this Capability may authorizeevery action type

Amounts are integers in minor units, like everywhere else in Delegus: 50000 in USD is $500.00. There are no decimals.

Action typeBudget members it accepts
commerce:purchasetotal, perPeriod, uses
api:calluses
commerce:quoteuses
commerce:acceptuses
commerce:fulfiluses

The Grant is malformed, and every request with it is refused at check P1 with GRANT_MALFORMED, if the budget:

Money budgets on commerce:quote and commerce:accept are an open question in the rc1 text (§16, item 12). Until that is settled, those types take uses only.

A relying party still on delegus-base-v1 does not know the budget key. For it, a Grant with a budget fails check P17 with UNKNOWN_CONSTRAINT. Budgets need a relying party on delegus-base-v3.

A Capability with a budget, as it appears in a Grant:

json
{
  "action": "commerce:purchase",
  "constraints": {
    "maxAmount": 50000,
    "currency": "USD",
    "budget": {
      "total": 150000,
      "perPeriod": { "amount": 100000, "period": "week" },
      "uses": 20
    }
  },
  "resources": ["computeco/orders/*"]
}

maxAmount still limits each single purchase. The budget limits the purchases added together.

Periods#

A period budget resets on a schedule that starts at the Grant's validFrom, in UTC. It does not follow the calendar week or month.

Spending from an earlier period does not count against the current one.

How a check spends#

Two steps handle a budget, and they sit at opposite ends of the decision.

  1. P21, the headroom check. After the ordinary checks (the signatures, dates, revocation, replay, action type, currency, amount and resource), Delegus reads the Grant's current counts and asks: does this action fit? It checks the dimensions in the order total, period, uses, and the first one without room fails. If the Grant has more than one Capability that could authorize the action, they are tried in Grant order and the first one with headroom, or the first with no budget, is the one used.
  2. T7, the spend. Only when every other check is true does Delegus spend: one atomic decrement of the chosen Capability's counts. Its answer is trust check T7.

The spend is the very last step of an evaluation, so a refusal never spends. In the spec's words, "a DENY never spends budget" (§5.3). A request that is replayed, revoked, expired, sent to the wrong relying party, or refused for any other reason is decided before P21 or skips it, and never touches the budget.

P21 itself runs only when every earlier check is true, including the trust checks T1, T2, T3 and the replay check T5. When any of them fails, P21 is "skipped", no counts are read, and the receipt carries no consumption evidence.

What each allowed action spends:

Action typetotal and period spenduses spend
commerce:purchasethe action's amount1
every other typenothing (no money budget exists)1

What each outcome looks like#

Every outcome below is a signed receipt returned with HTTP 200. None is an HTTP error. The spend and its receipt are recorded together or not at all (§5.4): if Delegus cannot record them, /verify answers HTTP 503 with no receipt and nothing spent.

SituationDecisionreasonChecksBudget
The action fits, the spend succeedsALLOWnullP21 true, T7 trueSpent
The action does not fit (total, period or uses)DENYAUTHORITY_EXHAUSTEDP21 false, T7 "skipped"Nothing spent
The action fitted at P21, but a request running at the same moment took the last headroom firstDENYAUTHORITY_EXHAUSTEDP21 true, T7 falseNothing spent by this request
Delegus could not read the counts for a budgeted CapabilityDENYSERVICE_UNAVAILABLEP21 falseNothing spent
Delegus could not reach the store to spendDENYSERVICE_UNAVAILABLET7 falseNothing spent
Any other check failedDENYthat check's reasonP21 and T7 "skipped" or not reachedNothing spent

Both AUTHORITY_EXHAUSTED cases come after the replay check T5, so the agent's Proof has been used. To try again, the agent signs a fresh Proof.

The same reason code, AUTHORITY_EXHAUSTED, comes from two checks. To tell them apart, look at which check is false in the receipt: protocol.checks.P21 or trust.checks.T7.

The receipt's consumption evidence#

When the matched Capability has a budget, or P21 failed on one, the receipt carries evidence.consumption:

json
"consumption": {
  "snapshot_hash": "sha256:...",
  "as_of": "2026-09-10T15:02:11.400Z",
  "capability_key_commitment": "sha256:...",
  "capability_index": 0,
  "consumed": "consumed"
}
FieldMeaning
snapshot_hashThe hash of the counts Delegus read for this Grant, the consumption snapshot, exactly as P21 used them
as_ofWhen those counts were read
capability_key_commitmentIdentifies which Capability was spent from, without revealing it. Only the receipt's parties can open it (see below)
capability_indexThe position of that Capability in the Grant's list
consumedThe answer of the spend, when it ran: consumed, exhausted or unavailable

The receipt names it capability_key_commitment (spec §6.2, §6.3, §R.6).

On a P21 refusal for lack of headroom, evidence.consumption also carries exhausted: total, period or uses, the first dimension without headroom (spec §6.2).

The counts themselves are not in the receipt. The receipt's relying party and Principal can fetch the snapshot with GET /decisions/{receipt_id}/disclosure, which returns it as consumption_snapshot; anyone else gets a 404. With the snapshot, P21 can be re-run offline. The snapshot looks like this:

json
{
  "grant_id": "urn:delegus:grant:01J9X4QK7M3Y2Z8N6P5R4T3V2W",
  "as_of": "2026-09-10T15:02:11.400Z",
  "version": 7,
  "capabilities": [
    { "key": "sha256:...", "spent_total": 57300, "spent_in_period": 0,
      "period_start": "2026-09-08T00:00:00Z", "uses_spent": 3 }
  ]
}

Beside the receipt, the /verify response returns authority (the matched Capability in plain text), authority_opening, and, when the receipt pins a capability-key commitment, capability_key_opening. The relying party uses them to confirm what the receipt pins. They are not part of the signed receipt.

Giving budget back: cancelled orders#

Budget comes back in one case only: the relying party reports that the order was cancelled.

When the outcome moved budget, the 201 response carries "consumption": { "cause": "restore", "version": <n> }.

Restores change the counts, not any receipt. A receipt signed before a restore still pins the snapshot it was decided against.

A cancelled outcome attestation on an ALLOW receipt restores what that receipt consumed, once; an attestation that corrects a cancelled one applies the spend again. No other outcome type touches consumption (spec §3.3.1). The trust configuration trust_0003 records this as restore_on.

Seeing the balance#

GET /grants/{id}/consumption returns, per Capability, the balances (spent_total, spent_in_period, uses_spent, remaining, limits, period_start) and the consumption log: events whose cause is consume, restore or restore_reversal, with their deltas and the receipt or attestation that caused each. It takes a Principal, organization or relying-party key; the API reference says who may read which Grant.

Worked example#

Acme gives its purchasing agent, procurement-7, a Grant to buy compute from ComputeCo. The Grant is valid from Wednesday 9 September 2026, 00:00 UTC. It has one Capability:

ComputeCo is a relying party on delegus-base-v3. Each time procurement-7 places an order, ComputeCo sends the Grant, the agent's Proof and the order to /verify.

#When (UTC)OrderP21 asksResultSpent after (total / this week)
1Thu 10 Sep$427.00 (42700)0 + 42700 ≤ 150000; 0 + 42700 ≤ 100000ALLOW, T7 consumed42700 / 42700
2Sat 12 Sep$450.00 (45000)42700 + 45000 ≤ 150000; 42700 + 45000 ≤ 100000ALLOW87700 / 87700
3Mon 14 Sep$200.00 (20000)total: 107700 ≤ 150000, fits; week: 87700 + 20000 = 107700 > 100000DENY AUTHORITY_EXHAUSTED at P21. Nothing spent87700 / 87700
4Wed 16 Sep, 10:00$200.00 (20000)A new week began at 00:00 on 16 Sep, so this week's spend is 0. 87700 + 20000 ≤ 150000; 0 + 20000 ≤ 100000ALLOW107700 / 20000

Order 3 was refused only because of the weekly cap: the total still had room. Its receipt shows protocol.checks.P21: false, trust.checks.T7: "skipped", and evidence.consumption with the snapshot it was checked against. procurement-7 can place the same order again once the next week starts, with a fresh Proof.

Now ComputeCo cancels order 2 and reports it:

bash
curl -s https://api.delegus.ai/decisions/drc_.../outcomes \
  -H "authorization: Bearer $DELEGUS_API_KEY" \
  -H "content-type: application/json" \
  -d '{"type":"cancelled","occurred_at":"2026-09-17T09:00:00Z"}'

Delegus restores order 2's spend: the total drops by 45000 to 62700. Order 2 was spent in the week that began 9 September, which is over, so the current week's spend stays at 20000. Acme's agent has $877.00 of its total left and $800.00 left this week.

Two orders arriving at the same moment for the last of a budget are settled by the spend itself. Both may pass P21, because each reads the counts before either has spent. The spend is designed as one atomic decrement, so the two cannot both spend the same headroom: the request that loses gets AUTHORITY_EXHAUSTED at T7, and nothing is spent for it.

Next#