Docs

How AgentLedger works

AgentLedger sits between an AI agent and its wallet. Every payment the agent wants to make is checked against your limits before anything is signed, and every decision is written to a hash-chained audit trail. Early access is by invitation from the waitlist.

Getting access

  1. Join the waitlist. We set up a workspace for you.
  2. Sign in at /panel with your email. Privy creates your wallet on Base.
  3. Send USDC to it (what the agent spends) and a little ETH on Base (network fees).
  4. Set your limits, then grant signing authority. Until you do, the agent cannot sign anything.

How a payment is governed

  1. Quote. The agent asks for the price first, without paying.
  2. Evaluate. The rules below run in order. The first one that fails decides.
  3. Record. The decision is written to the chain. An allowed payment reserves its amount here, so two payments at once cannot both spend a budget that fits one.
  4. Sign. Only an allowed payment is signed. On BLOCK or HOLD the key is never used.
  5. Settle and reconcile. The result is recorded with its transaction hash, and every day the wallet is compared against the chain.

A rule that cannot measure something it needs — a balance, a market price — blocks. “Could not look” is never treated as “fine”.

Your limits

Set from the panel. Every change is written to your chain, with your name, before it applies.

LimitRuleWhat it does
Max per actiontx.maxAmountUSDC the agent may spend in one payment.
Daily budgetbudget.dailyTotal USDC per day, across all actions.
Reserve floorbudget.reserveUSDC the agent will never spend. Must be at least the daily budget.
Approval thresholdapproval.thresholdAbove this, the agent stops and asks you.
Buy only belowmarket.price-bandThe highest oracle price at which the agent buys.
Max execution costswap.slippageAgainst the oracle, in basis points (100 = 1%).

What the agent may pay — network, token and payee — is not a limit but its identity, and cannot be edited from the panel. Today: USDC on Base, to the Uniswap V3 router.

The rules, in order

Evaluated top to bottom; the first that fails decides the verdict.

  1. 1gate.disabledThe pause switch is on. Nothing signs.
  2. 2quote.incompleteThe payment request is missing its amount, token, network or payee.
  3. 3network.allowlistThe network is not one this agent may pay on.
  4. 4asset.allowlistThe token is not one this agent may pay with.
  5. 5payee.allowlistThe payee is not on the allowlist.
  6. 6payee.resourceThe payee is allowed, but not for this resource.
  7. 7resource.allowlistThe resource is not one this agent may buy.
  8. 8price.expectedThe quoted price is above the agreed price for this resource.
  9. 9fees.sponsoredThe quote does not sponsor network fees, and the policy requires it.
  10. 10tx.maxAmountAbove your max per action.
  11. 11budget.dailyToday's budget would be exceeded.
  12. 12budget.reserveThe wallet would go below your reserve floor — or its balance could not be read.
  13. 13market.price-bandThe market price is outside the band you set — or could not be read.
  14. 14swap.slippageExecuting would cost more than your max execution cost.
  15. 15budget.overrideA one-off exception you granted covers this payment.
  16. 16approval.thresholdAbove your approval threshold: the agent waits for you (HOLD).
  17. 17policy.passWithin every limit: allowed.

Verdicts

ALLOW
Within every limit. The amount is reserved before anything is signed.
BLOCK
A rule said no. Nothing was signed.
HOLD
Waiting for your approval.
APPROVED / DENIED
Your decision on a HOLD.
SETTLED
Paid, with the transaction hash.
UNCONFIRMED
Authorized, but the payment could not be measured yet. The daily check resolves it.
FAILED
Authorized, but it did not go through.
RECONCILED
A daily check of the wallet against the chain.

Signing authority

To buy while you are away, the agent needs a signing permission on your wallet. You grant it from the panel, through Privy, and it is limited twice: by your limits above, and by a Privy policy that Privy itself enforces — today, the Uniswap router on Base only, a cap per buy, and an expiry 90 days out. Anything else our key asks Privy to sign is refused by Privy.

You can revoke it in one click. Withdrawals are signed by you with your own login — the agent's permission cannot reach them, and neither can the pause switch.

Verify the chain yourself

Each entry stores the hash of the previous one. Its own hash is SHA-256(prevHash + "|" + fields), where the fields are seq, ts, agentId, resource, method, amount, asset, network, payTo, verdict, rule, txHash, each written as key=value and joined with |. Change any of them — or remove an entry from the middle — and the chain stops verifying from there. The human-readable reason text is not part of the hash: the facts are.

This checks the public ledger of our own agents (the one on /ledger). Run it in Node 18+ or in the browser console on this site, or download it:

const res = await fetch("https://agentpayments.fi/api/x402/ledger?limit=50");
const { entries } = await res.json();
const FIELDS = ["seq", "ts", "agentId", "resource", "method", "amount",
                "asset", "network", "payTo", "verdict", "rule", "txHash"];
const sha256 = async (s) =>
  [...new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(s)))]
    .map((b) => b.toString(16).padStart(2, "0")).join("");

const chain = [...entries].sort((a, b) => a.seq - b.seq);
let prev = chain[0].prevHash;
for (const e of chain) {
  if (e.prevHash !== prev) throw new Error(`chain broken at seq ${e.seq}`);
  const payload = FIELDS.map((k) => `${k}=${e[k] ?? ""}`).join("|");
  if ((await sha256(`${prev}|${payload}`)) !== e.hash) throw new Error(`entry altered at seq ${e.seq}`);
  prev = e.hash;
}
console.log(`✓ ${chain.length} entries verify`);

What it proves: the entries you fetched link to each other and none was altered. What it does not prove: that nothing older was removed before the first entry you fetched — that needs the hash anchored somewhere else, which we have not built yet.