Allowances

Give a sub-account or another agent a USDG budget with a daily cap, a per-payment cap and an end date, and take back what is left.

In plain words

An allowance is a budget you give to a helper agent, with rules. Think of a prepaid card you hand to an assistant: it can spend, but only up to the amount on the card, only so much a day, only so much per purchase, and only until the end date.

The money stays yours. Whatever the helper has not spent, you can take back at any time. The rules are checked by maths (a zero-knowledge proof) on every payment, so the helper cannot break them.

Use one when you want an agent to pay for things on its own without handing it your whole balance.

The rules you can set

Only the total is required. Every other rule is optional; leave it out and there is no such limit.

RuleWhat it meansCommand lineLocal API field
Total budgetThe most the helper can ever spend from it.allow 50budget (text, e.g. "50")
Daily capThe most it can spend in one day. The day resets at midnight UTC (00:00), not 24 hours after the first payment.--daily-cap 10dailyCap
Max per paymentThe most any single payment can be.--max-per-tx 2maxPerTx
DaysHow long the budget lives. After that it cannot be spent. Default: no end date.--days 30days (a number)
Unlock daysThe budget trickles in evenly over this many days instead of all at once. Not more than days.--unlock-days 30unlockDays

Amounts are USDG. In the local API they are text ("2.5"); days, unlockDays and agentIndex are numbers.

Who gets the budget

A budget goes either to one of your own sub-accounts or to another agent. Pick by who will do the spending.

A sub-accountAnother agent
What it isA separate pocket of your own account, numbered 0 to 15. Same key, separate money and rules.A different agent with its own key, on this computer or anywhere else.
How you name it--agent-index 0 (default: the first free one)--to-code h081-address:… (its address code)
Who pays from itYou, with pay … --agent-index 0The other agent, with its own pay
Good forSplitting your own program's spending into separate pots, each with its own limits.A helper you do not fully trust: it can never touch your main balance, only this budget.
Can you take it back?Yes, what is leftYes, what is left

Give either --agent-index or --to-code, not both.

Your first budget, step by step

A budget is paid out of your private (shielded) balance, so deposit first. Granting needs about 0.1 SOL in the agent wallet for the proof, most of it refunded. Keep 0.15 SOL there if you use budgets.

  1. Have enough in your private balance

    Deposit at least the budget. Skip this if your balance already covers it.

    any terminal
    npx -y @h081lll/agent deposit 60 --yes
  2. See the plan

    Without --yes nothing is sent. The command shows what it would do, what it needs and what the public chain would see.

    1 · see the plan (nothing is sent)
    npx -y @h081lll/agent allow 50 --daily-cap 10 --max-per-tx 2 --days 30 --agent-index 0
    what it prints
    Plan (nothing was sent):
      allow 50 USDG
      sent by: agent key; SOL needed: 0.1
      proof: grant (download 51 MB)
      public on chain: that the agent wallet made a grant
      ! Sent by the agent key: the chain shows the agent wallet made a grant, not the amount, the rules or the recipient.
      details: {…}
    Add --yes to send it.
  3. Send it

    The same command with --yes. The first grant downloads the grant proof files (about 51 MB), once.

    2 · send it
    npx -y @h081lll/agent allow 50 --daily-cap 10 --max-per-tx 2 --days 30 --agent-index 0 --yes
  4. Check it

    Your balance now lists the budget with its id and rules. The id is what you use to pay from it or take it back.

    any terminal
    npx -y @h081lll/agent balance --fresh
    the allowance in the result (example)
    "allowances": [
      {
        "id": "8410…",
        "remaining": "50",
        "holder": "sub-account 0",
        "agentIndex": 0,
        "dailyCap": "10",
        "maxPerTx": "2",
        "expires": "2026-12-11T09:30:00.000Z",
        "reclaimable": true,
        "pledged": false
      }
    ]

Giving a budget to another agent

The other agent sends you its h081-address: code. It is safe to share: it lets people pay that agent or give it a budget, nothing else. The code has a checksum and a network tag, so a typo or a code made for another network is caught before anything is sent.

on the other agent's computer: print its code
npx -y @h081lll/agent address
on yours: check the code you were sent
npx -y @h081lll/agent check h081-address:…
see the plan, then add --yes to send
npx -y @h081lll/agent allow 20 --daily-cap 5 --max-per-tx 1 --days 14 --to-code h081-address:…

From then on the other agent sees the budget in its own balance and pays from it with its own pay. Your main balance stays out of its reach.

Paying from a budget

pay spends from a budget, never from the main balance. It picks a budget whose rules allow the amount right now. If none can, it says which rule blocks each one, and nothing is sent.

From your own sub-account

1 · see the plan (nothing is sent)
npx -y @h081lll/agent pay 2 --to-code h081-address:… --agent-index 0
what it prints
Plan (nothing was sent):
  pay 2 USDG to shielded
  sent by: agent key; SOL needed: 0.02
  proof: spend (download 19 MB)
  public on chain: that the agent wallet sent a bank transaction
  ! Pays from an allowance this agent holds, inside its caps; when none can pay, the error names the cap.
  ! A payment to an address code is sent by the agent key: the chain shows the agent wallet made a payment, not to whom or how much.
Add --yes to send it.
2 · send it
npx -y @h081lll/agent pay 2 --to-code h081-address:… --agent-index 0 --yes

Two kinds of payment

RecipientHowWhat the chain seesFee
An h081-address: code--to-code h081-address:…That your agent wallet made a payment. Not to whom, not how much.None
A normal Solana wallet--to <wallet>The wallet and the amount it received, not which account paid. Sent by the H081 relayer.0.05 USDG or 0.20%, whichever is more (the plan also shows a one-off charge if that wallet has no USDG account yet)
pay a normal wallet through the relayer (see the plan first: leave out --yes)
npx -y @h081lll/agent pay 2 --to <the shop's Solana wallet> --agent-index 0 --yes

When someone gave you the budget

If another agent gave your agent a budget, leave out --agent-index. To pick one budget among several, pass its id with --allowance.

pay from a budget you were given
npx -y @h081lll/agent pay 1 --to-code h081-address:… --yes
pay from one exact budget
npx -y @h081lll/agent pay 1 --to-code h081-address:… --allowance <id from balance> --yes

Taking back what is left

You stay the owner of every budget you give. reclaim moves what is left back into your private balance, at any time, without asking the helper. Budgets you own show "reclaimable": true in your balance.

1 · see the plan (nothing is sent)
npx -y @h081lll/agent reclaim <id from balance>
what it prints
Plan (nothing was sent):
  reclaim
  sent by: agent key; SOL needed: 0.02
  proof: spend (download 19 MB)
  public on chain: that the agent wallet sent a bank transaction
  details: {…}
Add --yes to send it.
2 · send it
npx -y @h081lll/agent reclaim <id from balance> --yes

Recipes

A personal assistant: 50 USDG for 30 days, 10 a day, 2 per purchase

Your assistant buys small things for you. It should never spend more than 10 USDG in a day or 2 USDG on one thing, and the budget should end after a month.

any terminal
# 1 · see the plan (nothing is sent)
npx -y @h081lll/agent allow 50 --daily-cap 10 --max-per-tx 2 --days 30 --agent-index 0
# 2 · send it
npx -y @h081lll/agent allow 50 --daily-cap 10 --max-per-tx 2 --days 30 --agent-index 0 --yes
# 3 · the assistant pays a shop privately, from sub-account 0
npx -y @h081lll/agent pay 1.5 --to-code h081-address:… --agent-index 0 --yes

Worst case, if the assistant goes wrong: at most 10 USDG a day, 2 at a time, never more than 50 in total. You can stop it at any moment by reclaiming the rest.

A research team: one budget per agent

Three agents work on a project: two researchers buy data, one summariser pays for small tools. Each gets its own budget, so one agent overspending cannot drain the others. Each agent runs npx -y @h081lll/agent address on its own machine and sends you its code.

any terminal
# one budget per agent, each with its own rules (run each without --yes first to see the plan)
npx -y @h081lll/agent allow 20 --daily-cap 5 --max-per-tx 1 --days 14 --to-code h081-address:…(researcher A) --yes
npx -y @h081lll/agent allow 20 --daily-cap 5 --max-per-tx 1 --days 14 --to-code h081-address:…(researcher B) --yes
npx -y @h081lll/agent allow 10 --daily-cap 2 --max-per-tx 0.5 --days 14 --to-code h081-address:…(the summariser) --yes

# when the project ends: list what is left, then take it back one by one
npx -y @h081lll/agent balance --fresh
npx -y @h081lll/agent reclaim <id from balance> --yes

If all three run inside your own program with your key, give them sub-accounts instead, and have each pay with its own --agent-index:

sub-accounts 1, 2 and 3
npx -y @h081lll/agent allow 20 --daily-cap 5 --max-per-tx 1 --days 14 --agent-index 1 --yes
npx -y @h081lll/agent allow 20 --daily-cap 5 --max-per-tx 1 --days 14 --agent-index 2 --yes
npx -y @h081lll/agent allow 10 --daily-cap 2 --max-per-tx 0.5 --days 14 --agent-index 3 --yes

A budget that trickles in

With --unlock-days the budget is not all spendable on day one: it unlocks evenly. 30 USDG over 30 days means about 1 USDG becomes spendable each day, and what has not unlocked yet cannot be spent early.

see the plan, then add --yes
npx -y @h081lll/agent allow 30 --days 30 --unlock-days 30 --max-per-tx 3 --agent-index 4

With the local API

Start the API with npx -y @h081lll/agent serve. Every write below returns a plan unless the body has "confirm": true. A confirmed write also needs an Idempotency-Key header, so a retry never sends twice. See Local API for the token and the response envelope.

POST/v1/allowances

Give a sub-account or another agent a budget.

Needs the tokenPlan first · sends only with "confirm": true#

FieldTypeMeaning
budgettext, requiredThe total, in USDG
dailyCaptextMost per day
maxPerTxtextMost per payment
daysnumberLifetime in days (default: no end)
unlockDaysnumberUnlock evenly over this many days
agentIndexnumber 0–15Which sub-account
toAddressCodetextAnother agent's h081-address: code (instead of agentIndex)
confirmtrue / falseWithout true: the plan only
macOS / Linux (bash)
TOKEN=$(cat ~/.h081/api-token)

# 1 · the plan: no "confirm", nothing is sent
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"budget":"50","dailyCap":"10","maxPerTx":"2","days":30,"agentIndex":0}' \
  http://127.0.0.1:8081/v1/allowances

# 2 · send it: "confirm": true and an Idempotency-Key
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: grant-assistant-1" \
  -d '{"budget":"50","dailyCap":"10","maxPerTx":"2","days":30,"agentIndex":0,"confirm":true}' \
  http://127.0.0.1:8081/v1/allowances
Windows PowerShell
$h = @{ Authorization = "Bearer $(Get-Content $HOME\.h081\api-token)"; "Idempotency-Key" = "grant-assistant-1" }

# 1 · the plan: no "confirm", nothing is sent
Invoke-RestMethod -Method Post -Headers $h -ContentType application/json -Body '{"budget":"50","dailyCap":"10","maxPerTx":"2","days":30,"agentIndex":0}' http://127.0.0.1:8081/v1/allowances

# 2 · send it
Invoke-RestMethod -Method Post -Headers $h -ContentType application/json -Body '{"budget":"50","dailyCap":"10","maxPerTx":"2","days":30,"agentIndex":0,"confirm":true}' http://127.0.0.1:8081/v1/allowances
Python (requests)
import pathlib, requests

API = "http://127.0.0.1:8081/v1"
TOKEN = (pathlib.Path.home() / ".h081" / "api-token").read_text().strip()
H = {"Authorization": f"Bearer {TOKEN}"}
body = {"budget": "50", "dailyCap": "10", "maxPerTx": "2", "days": 30, "agentIndex": 0}

plan = requests.post(f"{API}/allowances", json=body, headers=H).json()
print(plan["status"], plan["result"])          # "plan": nothing was sent

sent = requests.post(f"{API}/allowances", json={**body, "confirm": True},
                     headers={**H, "Idempotency-Key": "grant-assistant-1"}).json()
print(sent["status"], sent["result"])          # "sent"
TypeScript (SDK, no server needed)
import { H081 } from '@h081lll/agent';

const bank = await H081.open();
const rules = { dailyCap: '10', maxPerTx: '2', days: 30, agentIndex: 0 };

const plan = await bank.allow('50', rules);      // the plan: nothing is sent
console.log(plan.result);

const sent = await bank.allow('50', { ...rules, confirm: true, idempotencyKey: 'grant-assistant-1' });
console.log(sent.result);
response without confirm (example)
{
  "ok": true,
  "network": "mainnet-beta",
  "status": "plan",
  "result": {
    "op": "allow",
    "usdg": "50",
    "fee": "0",
    "solNeeded": "0.1",
    "circuit": "grant",
    "download": "51 MB",
    "publicOnChain": ["that the agent wallet made a grant"],
    "sentBy": "agent key",
    "warnings": ["Sent by the agent key: the chain shows the agent wallet made a grant, not the amount, the rules or the recipient."],
    "details": { … },
    "next": "Nothing was sent. Send it again with confirm: true (CLI: --yes) to do it."
  },
  "requestId": "…"
}

POST/v1/pay

Pay from a budget this agent holds.

Needs the tokenPlan first · sends only with "confirm": true#

Body: usdg, exactly one of to (a wallet) or toAddressCode, and optionally allowanceId, agentIndex and confirm.

macOS / Linux (bash)
# 1 · the plan: no "confirm", nothing is sent
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"usdg":"2","toAddressCode":"h081-address:…","agentIndex":0}' \
  http://127.0.0.1:8081/v1/pay

# 2 · send it
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: pay-coffee-1" \
  -d '{"usdg":"2","toAddressCode":"h081-address:…","agentIndex":0,"confirm":true}' \
  http://127.0.0.1:8081/v1/pay
Windows PowerShell
# 1 · the plan: no "confirm", nothing is sent
Invoke-RestMethod -Method Post -Headers $h -ContentType application/json -Body '{"usdg":"2","toAddressCode":"h081-address:…","agentIndex":0}' http://127.0.0.1:8081/v1/pay

# 2 · send it
$h["Idempotency-Key"] = "pay-coffee-1"
Invoke-RestMethod -Method Post -Headers $h -ContentType application/json -Body '{"usdg":"2","toAddressCode":"h081-address:…","agentIndex":0,"confirm":true}' http://127.0.0.1:8081/v1/pay
Python (requests)
body = {"usdg": "2", "toAddressCode": "h081-address:…", "agentIndex": 0}
print(requests.post(f"{API}/pay", json=body, headers=H).json()["status"])      # "plan"
paid = requests.post(f"{API}/pay", json={**body, "confirm": True},
                     headers={**H, "Idempotency-Key": "pay-coffee-1"}).json()
TypeScript
const pay = { toAddressCode: 'h081-address:…', agentIndex: 0 };
console.log((await bank.pay('2', pay)).result);                      // the plan
await bank.pay('2', { ...pay, confirm: true, idempotencyKey: 'pay-coffee-1' });

POST/v1/allowances/{id}/reclaim

Take back what is left of a budget you gave.

Needs the tokenPlan first · sends only with "confirm": true#

Put the current id from GET /v1/balance in place of {id}. The body is {} for the plan, or {"confirm": true} to send.

macOS / Linux (bash)
# the id comes from GET /v1/balance (allowances[].id, where "reclaimable": true)
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{}' http://127.0.0.1:8081/v1/allowances/<id>/reclaim
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: reclaim-1" \
  -d '{"confirm":true}' http://127.0.0.1:8081/v1/allowances/<id>/reclaim
Windows PowerShell
$id = "<id from balance>"
$h["Idempotency-Key"] = "reclaim-1"
Invoke-RestMethod -Method Post -Headers $h -ContentType application/json -Body '{}' "http://127.0.0.1:8081/v1/allowances/$id/reclaim"
Invoke-RestMethod -Method Post -Headers $h -ContentType application/json -Body '{"confirm":true}' "http://127.0.0.1:8081/v1/allowances/$id/reclaim"
Python (requests)
bal = requests.get(f"{API}/balance", params={"fresh": "1"}, headers=H).json()["result"]
mine = [a for a in bal["allowances"] if a["reclaimable"]]
for a in mine:
    print(a["id"], a["holder"], a["remaining"])

aid = mine[0]["id"]
print(requests.post(f"{API}/allowances/{aid}/reclaim", json={}, headers=H).json()["status"])   # "plan"
requests.post(f"{API}/allowances/{aid}/reclaim", json={"confirm": True},
              headers={**H, "Idempotency-Key": "reclaim-1"})
TypeScript
const { allowances } = await bank.balance({ fresh: true });
const mine = allowances.filter((a) => a.reclaimable);
console.log((await bank.reclaim(mine[0].id)).result);               // the plan
await bank.reclaim(mine[0].id, { confirm: true, idempotencyKey: 'reclaim-1' });

The API also streams allowance.changed events on GET /v1/events, so a dashboard can update when a budget is spent or reclaimed.

With Claude

Once Claude has the H081 tools (set-up), ask in plain words. For a new budget or a reclaim, Claude first gets the plan, which sends nothing, and should show it to you before sending. A payment from a budget is sent when Claude makes it, so the budget's rules are your safety limit: keep the budgets you let Claude spend from small.

Example conversation · wording variesSetting up an assistant's budget
  1. You

    Give my sub-account 0 a budget of 50 USDG for 30 days. At most 10 USDG a day and 2 USDG per purchase.

  2. Claude

    uses h081_allowance_grant

    Here is the plan. Nothing has been sent yet.

    50 USDG from your private balance to sub-account 0, for 30 days, at most 10 USDG a day and 2 USDG per payment. It needs about 0.1 SOL for fees, most of it refunded. The chain will only show that your agent wallet made a grant, not the amount, the rules or who got it.

    Shall I send it?

  3. You

    Yes, send it.

  4. Claude

    uses h081_allowance_grantuses h081_bank_balance

    Done. Sub-account 0 now has 50 USDG, with 10 a day and 2 per payment, until 11 December.

Example conversation · wording variesTaking it back
  1. You

    How much is left in the budgets I gave out? Take back whatever sub-account 0 has not spent.

  2. Claude

    uses h081_bank_balanceuses h081_allowance_reclaim

    Sub-account 0 has 41.5 USDG left. Reclaiming would move 41.5 USDG back into your private balance. Nothing has been sent yet. Go ahead?

  3. You

    Go ahead.

  4. Claude

    uses h081_allowance_reclaim

    Reclaimed. 41.5 USDG is back in your private balance.

Prompts that work well:

  • "Give the agent with code h081-address:… a budget of 20 USDG for 14 days, 5 a day, 1 per payment. Show me the plan first."
  • "Pay 2 USDG from sub-account 0's budget to h081-address:…."
  • "List every budget I own, with what is left and when it ends."

What the public chain sees

You doPublic on SolanaStays private
Give a budgetThat your agent wallet made a grantThe amount, the rules, who got it
Pay to an h081-address: codeThat your agent wallet made a paymentTo whom and how much
Pay to a normal walletThat wallet and the amount it received (sent by the relayer)Which account or budget paid
Take a budget backThat your agent wallet sent a bank transactionThe amount

More in Privacy and safety.

Limits and common problems

SOL
Granting needs about 0.1 SOL (mostly refunded); paying and reclaiming about 0.02 SOL. Short of it, you get needs_sol and nothing is sent.
Not enough in the private balance
insufficient_shielded: deposit first.
No budget can pay
allowance_blocked: every budget is used up, ended, at its daily cap, not unlocked far enough yet, pledged to a loan, or the payment is over its per-payment cap. The message names the rule for each budget. Nothing is sent.
Your own ceilings
Set H081_MAX_GRANT and H081_MAX_PAY (in base units: 1 USDG = 1000000) to refuse any budget or payment above that, before anything is proven. A refusal comes back as over_operator_ceiling.
How many
Sub-accounts are numbered 0 to 15.

Every code is in Errors and fixes.

Next

New here? The 5-minute quickstart does a first private payment end to end. The package is @h081lll/agent on npm. Questions: @H081CAP on X.