pahz documentation

How to open a tab, let an agent pay per call over x402, read the meter, and settle on Base. Sections 01–05 get you to a first paid call.

01Overview

pahz is a client-side payment layer for AI agents. An agent makes HTTP calls through the pahz fetch wrapper; when a service answers 402 Payment Required (x402), pahz checks the agent's tab — budget, rate cap, allowlist, price band — and either signs a USDC payment and retries, or holds the call and notifies the owner. Payments are batched into settlements on Base with verifiable receipts. Every payment and every hold is a line on the meter.

The agent never holds a key. The owner can close the tap at any time without stopping the agent.

02Vocabulary

termmeaning
tabA funded USDC account on Base with rules, assigned to one agent.
rulebudget, rate cap, allowlist, price band, expiry.
holdA call pahz refused to pay for, pending owner action.
meterThe per-tab log of payments and holds.
settlementA batch of payments written to Base as one transaction.
receiptThe settlement's on-chain hash covering its meter lines.
tapThe open/closed state of a tab.
signerThe pahz component that holds tab keys and signs authorizations.

03Quickstart

import { pahz } from "@pahz/sdk";

const tab = await pahz.tab.open({
  budget: 5,                             // USDC
  rate: { perMin: 60 },
  allow: ["search.api", "infer.llm", "tag:verified"],
  band: { "infer.llm": 0.02, "*": 0.005 }
});

const fetch = pahz.fetch(tab);           // drop-in
const r = await fetch("https://search.api/q?text=…");   // pays 0.002 if asked
console.log(await pahz.meter(tab).last(5));

04Install

npm install @pahz/sdk        # agent side
npm install @pahz/serve      # service side (x402 helper)

Node 18+ or any runtime with fetch. The SDK talks to the pahz signer API and to Base via the RPC in PAHZ_RPC.

05Fund a tab

pahz.tab.open() deploys a tab contract on Base and returns its address. Send USDC to it from your wallet, or pass fundFrom to have the SDK request a signature. The tab is closed until funded; the first payment opens it.

Fund with what you'd accept losing to a bug. Set the rate cap so that a bug takes an hour to reach the budget, not a second.

06Tabs

A tab belongs to one owner and is assigned to one agent identity (an API key issued by the signer). Tabs are cheap; use one per agent. A tab's rules are set at open and changed only by the owner. The agent can read its own rules but not write them.

07Rules

ruleenforced whereon violation
budgettab contract + signerhold; settlement reverts if exceeded
rate capsignerhold with retry-after
allowlisttab contract + signerhold; owner notified
price bandsignerhold; owner notified
expirytab contractbalance returns to owner

08Holds

A hold returns a structured 402-held to the agent with the rule that held it and, where applicable, a retry-after. Holds appear in the owner's inbox with actions: allow once, allow always (edits the rule), raise band, ignore. Allowing once re-signs and retries the original call if the agent is still waiting.

09The meter

{ "tab": "0x41…", "n": 1832, "at": "2026-09-14T10:02:11Z",
  "service": "search.api", "path": "/q", "price": 0.002,
  "latency_ms": 41, "nonce": "…", "settlement": 77 }
{ "tab": "0x41…", "n": 1833, "held": "allowlist", "service": "newapi.io" }

Meter lines are append-only and exportable (JSON, CSV). Each paid line carries the settlement id it was included in.

10Settlements

Payments are signed as USDC transfer authorizations (EIP-3009 style) and batched. A settlement is triggered every 5 minutes or every 200 payments, whichever first, and moves USDC from the tab to each service's pay-to address in one transaction on Base.

11Receipts

Each settlement emits a receipt: settlement id, tab, total, count, and a Merkle root over its meter lines. Anyone with the meter export can verify a line was included; the owner can reconcile the meter to the chain to the cent.

12The tap

pahz tab close <id> flips the tab to closed with one owner signature. The signer stops signing immediately; the agent receives 402-held: tap_closed on every paid call and keeps running. open reverses it. Closing does not cancel pending settlement of already-signed payments.

13Service directory

Services can register their x402 endpoints with a name, tags and a published price schedule. Allowlists may reference tags (tag:verified, tag:search). Listing is optional; unlisted services can still be allowlisted by host.

14Architecture

agent ── pahz.fetch ──▶ service (x402)
             │ 402 + price
             ▼
        pahz signer ── rules (rate, band) ── sign auth ──▶ retry ──▶ 200
             │ meter line
             ▼
        settler ── batch ──▶ Tab contract (Base) ── USDC ──▶ services
                              ▲ rules (budget, allowlist, expiry)

15x402 flow

  1. Agent request → service replies 402 with X-Payment-Required (amount, asset, payTo, network, nonce, facilitator).
  2. pahz signer validates rules, signs an authorization for the amount from the tab.
  3. Retry with X-Payment header. Service or facilitator verifies; serves 200.
  4. Authorization is queued for settlement.

pahz implements the client side of the x402 spec unchanged; services need nothing pahz-specific.

16Signer

The signer holds tab signing keys in an HSM-backed service and exposes an API to agents authenticated by per-agent keys. It enforces rate caps and price bands, writes meter lines, and queues authorizations. It cannot move funds outside a settlement, and settlements are bounded by the tab contract. Self-hosting the signer is supported.

17Tab contract on Base

A minimal contract per tab holding USDC. Encodes owner, budget (balance), allowlist root, expiry, and tap state. settle() accepts a batch of authorizations and reverts if any pay-to is outside the allowlist or the total exceeds balance. Owner can topUp, withdraw, setRules, setTap.

18Settlement batching

Batches are per tab. Gas is amortised across all payments in the batch; at 200 payments per settlement the per-payment cost is a small fraction of a cent. Batches are ordered by nonce; a failed batch is retried without the offending authorization and the failure is metered.

19Fees

  • Per payment: a small flat fee added to the settlement, covering gas and signer operation.
  • Holds: free.
  • Tab open / close / top-up: gas only.

Live values: pahz fees.

20Security model

if this is compromisedattacker canattacker cannot
the agentspend the tab within its rulesexceed budget, rate or allowlist; extract a key
the signersign authorizations within rate/bandsettle outside allowlist or above balance (contract reverts)
the owner keyeverything— use a hardware wallet or multisig

21pahz.tab.open()

pahz.tab.open(o: {
  budget: number;                 // USDC
  rate?: { perMin?: number; perHour?: number; perDay?: number };
  allow: string[];                // hosts or "tag:…"
  band?: Record<string, number>;  // max price per call; "*" default
  expires?: string;               // "7d"
  fundFrom?: Address;
}): Promise<Tab>                  // { id, address, agentKey }

22pahz.fetch()

const fetch = pahz.fetch(tab);
const r = await fetch(url, init);
// on hold: r.status === 402, r.headers.get("x-pahz-held") === "allowlist" | "band" | "rate" | "budget" | "tap_closed"
// r.headers.get("retry-after") set for rate holds

23pahz.meter()

pahz.meter(tab).last(n)
pahz.meter(tab).range(from, to)
pahz.meter(tab).byService()          // totals
pahz.meter(tab).export("csv")

24pahz.tab.close() / topUp()

await pahz.tab.close(tab)            // owner signature
await pahz.tab.open(tab)             // re-open
await pahz.tab.topUp(tab, 10)        // USDC
await pahz.tab.withdraw(tab)         // remaining balance to owner

25Service-side helper

import { paid } from "@pahz/serve";
app.get("/q", paid({ price: 0.002, payTo: "0x…" }), handler);
// adds x402 402 response + verification; nothing pahz-specific

26Contract interface

interface IPahzTab {
  function owner() external view returns (address);
  function rules() external view returns (Rules memory);   // allowlistRoot, expiry, tap
  function settle(Auth[] calldata auths, bytes32[] calldata proofs) external; // settler only
  function setRules(Rules calldata r) external;             // owner
  function setTap(bool open) external;                      // owner
  function topUp(uint256 amt) external;
  function withdraw(uint256 amt) external;                  // owner
}

27Errors

held reasonmeaningowner action
allowlistservice not allowedallow once / always
bandprice above max for serviceraise band / allow once
raterate cap reachednone; agent retries after
budgettab balance insufficienttop up
tap_closedowner closed the tapopen
expiredtab past expiryopen a new tab

28Guide: give a research agent a tab

  1. Open a tab with 5 USDC, rate.perMin: 60, allowlist [search.api, infer.llm, tag:verified].
  2. Replace the agent's fetch with pahz.fetch(tab).
  3. Run a task. Watch pahz meter --follow.
  4. Review holds in the inbox; allow the ones that make sense.
  5. Top up when the meter says so, or set expiry and let it end.

29Guide: fleet budgets

Open one tab per agent under a team owner (multisig). Tag tabs with team:research. pahz meter --team research --by service rolls spend up. Set per-tab expiry to the sprint length so budgets do not linger.

30Guide: add x402 to your service

  1. npm i @pahz/serve (or any x402 server library).
  2. Wrap the route with paid({price, payTo}).
  3. Register in the directory with tags so tabs can allowlist you by tag.
  4. Receive USDC at settlement on Base. No invoices.

31Safety checklist

  • Budget = acceptable loss.
  • Rate cap so a bug takes an hour, not a second.
  • Explicit allowlist; use tags sparingly.
  • Price bands on inference endpoints especially.
  • Owner key on hardware or multisig.
  • Set expiry on every tab.

32FAQ

Does the agent hold a key?

No. It holds an agent key that lets it ask the signer to pay from its tab, within rules.

What if a service is down mid-payment?

The authorization is not consumed; the call fails normally and nothing settles.

Can I use it without x402 services?

No. pahz pays x402 endpoints only.

Other chains?

Base only today, where x402 settles in USDC.

Can I self-host the signer?

Yes; the tab contract bounds it either way.

33Glossary

allowlist — permitted services. band — max price per call. hold — refused call awaiting owner. meter — per-tab log. receipt — settlement proof. settlement — batched on-chain payment. signer — pahz key service. tab — funded, ruled account. tap — open/closed state. x402 — HTTP payment-required protocol.