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.
| Rule | What it means | Command line | Local API field |
|---|---|---|---|
| Total budget | The most the helper can ever spend from it. | allow 50 | budget (text, e.g. "50") |
| Daily cap | The most it can spend in one day. The day resets at midnight UTC (00:00), not 24 hours after the first payment. | --daily-cap 10 | dailyCap |
| Max per payment | The most any single payment can be. | --max-per-tx 2 | maxPerTx |
| Days | How long the budget lives. After that it cannot be spent. Default: no end date. | --days 30 | days (a number) |
| Unlock days | The budget trickles in evenly over this many days instead of all at once. Not more than days. | --unlock-days 30 | unlockDays |
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-account | Another agent | |
|---|---|---|
| What it is | A 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 it | You, with pay … --agent-index 0 | The other agent, with its own pay |
| Good for | Splitting 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 left | Yes, 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.
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 --yesSee the plan
Without
--yesnothing 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 0what 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.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 --yesCheck 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 --freshthe 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.
npx -y @h081lll/agent addressnpx -y @h081lll/agent check h081-address:…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
npx -y @h081lll/agent pay 2 --to-code h081-address:… --agent-index 0Plan (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.npx -y @h081lll/agent pay 2 --to-code h081-address:… --agent-index 0 --yesTwo kinds of payment
| Recipient | How | What the chain sees | Fee |
|---|---|---|---|
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) |
npx -y @h081lll/agent pay 2 --to <the shop's Solana wallet> --agent-index 0 --yesWhen 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.
npx -y @h081lll/agent pay 1 --to-code h081-address:… --yesnpx -y @h081lll/agent pay 1 --to-code h081-address:… --allowance <id from balance> --yesTaking 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.
npx -y @h081lll/agent reclaim <id from balance>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.npx -y @h081lll/agent reclaim <id from balance> --yesRecipes
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.
# 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 --yesWorst 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.
# 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> --yesIf all three run inside your own program with your key, give them sub-accounts instead, and have each pay with its own --agent-index:
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 --yesA 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.
npx -y @h081lll/agent allow 30 --days 30 --unlock-days 30 --max-per-tx 3 --agent-index 4With 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.
| Field | Type | Meaning |
|---|---|---|
budget | text, required | The total, in USDG |
dailyCap | text | Most per day |
maxPerTx | text | Most per payment |
days | number | Lifetime in days (default: no end) |
unlockDays | number | Unlock evenly over this many days |
agentIndex | number 0–15 | Which sub-account |
toAddressCode | text | Another agent's h081-address: code (instead of agentIndex) |
confirm | true / false | Without true: the plan only |
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$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/allowancesimport 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"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);{
"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.
Body: usdg, exactly one of to (a wallet) or toAddressCode, and optionally allowanceId, agentIndex and confirm.
# 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# 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/paybody = {"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()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.
Put the current id from GET /v1/balance in place of {id}. The body is {} for the plan, or {"confirm": true} to send.
# 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$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"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"})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.
- 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.
- Claude
uses
h081_allowance_grantHere 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?
- You
Yes, send it.
- Claude
uses
h081_allowance_grantusesh081_bank_balanceDone. Sub-account 0 now has 50 USDG, with 10 a day and 2 per payment, until 11 December.
- You
How much is left in the budgets I gave out? Take back whatever sub-account 0 has not spent.
- Claude
uses
h081_bank_balanceusesh081_allowance_reclaimSub-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?
- You
Go ahead.
- Claude
uses
h081_allowance_reclaimReclaimed. 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 do | Public on Solana | Stays private |
|---|---|---|
| Give a budget | That your agent wallet made a grant | The amount, the rules, who got it |
Pay to an h081-address: code | That your agent wallet made a payment | To whom and how much |
| Pay to a normal wallet | That wallet and the amount it received (sent by the relayer) | Which account or budget paid |
| Take a budget back | That your agent wallet sent a bank transaction | The 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_soland 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_GRANTandH081_MAX_PAY(in base units: 1 USDG = 1000000) to refuse any budget or payment above that, before anything is proven. A refusal comes back asover_operator_ceiling. - How many
- Sub-accounts are numbered 0 to 15.
Every code is in Errors and fixes.
Next
- AdvancedLoansBorrow against a budget, or lend to one.
- ReferenceCommand lineEvery command and flag.
- SafetyPrivacy and safetyWhat the chain can see, and how to keep your key safe.
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.