One trace.
Three rails.
Every agent.
The AgentWallet Protocol is the layer that makes agent commerce auditable. Every Company, Principal and Agent gets a portable on-chain identity. Every payment — fiat, card or crypto — is authorized by a signed mandate, evaluated against policy, settled on its native rail, and written to a single ledger row anchored on Base. Same trace ID across ACH, Mastercard and USDC.
On-chain trace
Every settled payment carries a 32-byte trace ID. The mandate hash is anchored on Base; the rail provider returns a settlement reference; both land in the same ledger row. Audit can replay any payment end-to-end.
Portable agent score
0–1000 reputation derived from settled volume, dispute rate, mandate compliance and Principal KYB tier. Counterparties read the score over MCP before accepting a 402.
One model, three rails
Fiat (ACH, SEPA, Faster Payments, SWIFT), card (virtual Mastercard, push-to-card) and crypto (USDC, EURC, x402, on-chain transfer) all use the same Mandate → Policy → Rail pipeline.
ERC-native
ERC-20 for stablecoin balances, ERC-721 / ERC-1155 for agent identity NFTs, EIP-712 typed-data signing, EIP-3009 for x402, EIP-2612 permits, ERC-4337 for gas-sponsored agent ops.
AP2 mandates
IntentMandate, CartMandate and PaymentMandate are signed by the Principal's WebAuthn key. The Agent never holds a key that can move money on its own.
Immutable audit
Every mandate, policy decision, retry, partial settle, refund and reversal is appended to the trace. Nothing is mutated; corrections are new entries that reference the prior trace.
Company → Principal → Agent
Three identity tiers, programmed into the protocol. Money never moves outside this hierarchy. KYB lives at the Company. Authority lives at the Principal. Execution lives at the Agent.
Company (tenant)
- Who
- the legal entity. KYB-verified, beneficial owners on file, treasury accounts attached.
- Holds
- master fiat float, USDC treasury wallet, billing relationship with Payouts.com, default policy tree.
- On-chain
- a single registry entry on Base — company_id → eoa — that anchors all child Principals and Agents.
- Programmable
- create / disable Principals, set tenant-wide spending caps, rotate signing keys, export the full trace.
Principal (human)
- Who
- a human user inside the Company. KYC-verified. Holds the WebAuthn key that signs every AP2 mandate.
- Holds
- a sub-budget allocated by the Company, a list of Agents they own, a personal policy.
- On-chain
- principal_id → eoa, parented to a company_id. Public key is the AP2 signer.
- Programmable
- mint Agents, sign IntentMandates, approve high-value CartMandates, revoke any Agent in <1s.
Agent (machine)
- Who
- an autonomous AI agent. Holds an API key (pak_…) and an MCP endpoint.
- Holds
- its own fiat wallet, USDC wallet, virtual card, inbox, phone number — all scoped to one Principal.
- On-chain
- agent_id → eoa, parented to a Principal. ERC-721 identity NFT optional for cross-platform portability.
- Programmable
- spend within its envelope, pay/receive x402, call MCP tools, sign EIP-3009 authorizations, accrue an on-chain score.
Identity → Mandate → Money
Every payment traverses three layers, in order. You can swap providers inside any layer; you cannot skip a layer. This is the contract.
Identity & Trust
Company, Principal and Agent registries. KYB at the Company tier; KYC at the Principal tier; software identity (PAK + ERC-721 NFT) at the Agent tier.
Mandates & Policy
The Agent proposes a payment. The Principal (or pre-signed mandate) authorizes it. The Spending Policy engine evaluates per-call, per-day, per-counterparty, per-rail caps. The decision is signed.
Money Movement
The signed mandate is dispatched to the matching rail. Fiat goes through Payouts.com's regulated network. Cards through the issuer. USDC and stablecoins on Base / Ethereum / Polygon / Arbitrum / Optimism.
One trace ID.
Every rail. Forever.
The trace is the canonical identifier of an agent payment. It is generated at mandate time, anchored on Base, and carried by every downstream system — fiat, card, or crypto — so the audit is reconstructible from a single key.
What's in a trace
- trace_id
- 32 bytes, keccak256(mandate_json). Globally unique. Anchored on Base.
- mandate
- the signed AP2 PaymentMandate or x402 EIP-3009 authorization.
- actors
- (company_id, principal_id, agent_id, counterparty_id).
- policy_decision
- which Spending Policy approved it, with the matched rule ID and the cap consumed.
- rail
- one of ach, sepa, fps, swift, card_push, card_virtual, usdc_base, usdc_eth, x402, internal.
- settlement
- the rail-native reference: tx hash, ACH trace #, card auth code, MoneySend transaction ID.
- state_history
- append-only log of proposed → authorized → submitted → settled / failed / refunded / reversed.
- score_delta
- the contribution this payment made to the Agent's on-chain score.
trace = {
trace_id: "0xa3f1…7c",
company_id: "co_42",
principal_id: "pr_9",
agent_id: "ag_7b",
mandate: { type: "PaymentMandate", sig: "0x…", anchor_tx: "0x9f2a…" },
policy: { rule: "per_call_max_50_usd", consumed: "12.40" },
rail: "usdc_base", // or "ach", "card_virtual", "x402", …
settlement: "0x4d2…e1", // or "ACH-2026-0512-000871", "MC-AUTH-71B"
state: "settled",
score_delta: +3
}Agents earn reputation.
Counterparties read it before accepting payment.
Every Agent has a public 0–1000 score that decays without activity and grows with clean settled volume. Counterparties — MCP servers, x402 endpoints, marketplace vendors — query the score before accepting a 402, just like a card network checks a BIN.
| Signal | Weight | Direction |
|---|---|---|
| Cumulative settled volume (USD-equiv, 90d) | +250 max | + |
| Successful settlements / proposed mandates ratio | +200 max | + |
| Disputes / chargebacks (rolling 180d) | −300 max | - |
| Mandate revocations / fraud flags | −250 max | - |
| Principal KYB tier (Tier 1 / 2 / 3) | +150 max | + |
| Agent age (days since identity issuance) | +100 max | + |
| Policy compliance (declared vs actual spend pattern) | +150 max | + |
| Inactivity decay (no settled volume in 30d) | −2 / day | - |
The score is published as an ERC-1155 metadata field on the Agent's identity NFT and exposed as an MCP tool: agentwallet.score(agent_id) → { score, tier, last_updated, anchor_tx }. Counterparties can require a minimum score for an Agent to call their service.
Connects to every ERC standard agents actually need.
The protocol is opinionated about what an agent needs, and unopinionated about which chain it lives on. Every relevant ERC is mapped to a primitive in the Mandate → Policy → Rail pipeline.
| Standard | What it gives the agent | Where the protocol uses it |
|---|---|---|
| ERC-20 | USDC, EURC, USDT and 50+ stablecoin balances | Crypto wallet, x402 settlement |
| ERC-721 | Unique identity NFT per Agent — portable across platforms | Agent identity, score metadata anchor |
| ERC-1155 | Multi-token (license, capability, credential) bundles | Capability grants, score history |
| EIP-712 | Typed-data signing (human-readable signatures) | All AP2 mandates and policy decisions |
| EIP-2612 | ERC-20 permit — no separate approve tx | Gas-efficient stablecoin spends |
| EIP-3009 | transferWithAuthorization — signed transfer over HTTP | x402 settlement primitive |
| ERC-4337 | Account abstraction — gas sponsorship, batched ops | Gas-sponsored agent transactions |
| ERC-5267 | EIP-712 domain discovery | Cross-platform mandate verification |
Fiat · Card · Crypto — same protocol, different rail.
The differences your engineers actually care about. Same Mandate. Same Policy. Same trace. Different settlement physics. Pick the right rail per call — or let the policy engine route automatically.
| Property | Fiat (ACH / SEPA / FPS / SWIFT) | Card (virtual Mastercard / push-to-card) |
|---|---|---|
| Settlement time | seconds (FPS) → 3 days (ACH/SEPA) → 1–5d (SWIFT) | authorization in ms; capture in 1–3 days |
| Reversibility | ACH return up to 60d; SEPA recall window | chargebacks up to 120d |
| Per-call cost | $0.20 – $5 fixed | 2.0 – 3.5% + fixed interchange |
| Identity surface | bank account + name match | PAN, expiry, CVV, 3DS challenge |
| M2M friendliness | batch-friendly; not per-API-call | good for SaaS subscriptions; bad for sub-cent |
| Geo coverage | 208 countries via Payouts.com | ~210 countries, MC + push-to-card |
| Property | Crypto (USDC / EURC / x402 / on-chain) | How the protocol abstracts it |
|---|---|---|
| Settlement time | ~2s on Base, ~12s on Ethereum mainnet | Mandate → settlement state in single API call |
| Reversibility | irreversible — refund is a new tx | Trace links original + refund as one logical record |
| Per-call cost | ~$0.001 (Base L2) → $1+ (mainnet) | Gas sponsored by AgentWallet, capped per agent |
| Identity surface | EOA address + EIP-3009 signature | Wrapped by Agent identity NFT + Principal mandate |
| M2M friendliness | native — sub-cent payments per API call | x402 is the default for agent ↔ agent / agent ↔ API |
| Geo coverage | global, no banking rails required | Off-ramp to fiat in 14 countries via Payouts.com |
Every Principal. Every Agent.
The same six verbs.
The protocol exposes the same six verbs regardless of identity tier. The Principal API is REST + OIDC. The Agent API is the same shape, plus an MCP wrapper so agents call them as tools.
// 1. Identity
company.create({ legal_name, kyb_docs }) → company_id
company.principal.create({ company_id, name, email, webauthn_pubkey }) → principal_id
principal.agent.create({ principal_id, name, policy }) → { agent_id, pak, mcp_url }
// 2. Mandate (signed by the Principal's WebAuthn key)
principal.mandate.intent({ agent_id, intent, max_value, expires_at, sig }) → mandate_id
agent.mandate.cart({ items, counterparty }) → cart_id // requires Principal sig if > cap
agent.mandate.payment({ cart_id, rail }) → payment_mandate_id
// 3. Policy (evaluated automatically, no call needed; query-able)
policy.evaluate({ mandate, agent_id }) → { decision: "allow"|"deny"|"escalate", rule, cap_consumed }
policy.upsert({ scope: "company"|"principal"|"agent", rules: [...] })
// 4. Rail (the only verb that touches money)
rail.dispatch({ payment_mandate_id }) → { rail, settlement_ref, trace_id, state }
// 5. Trace (single source of truth for audit)
trace.get(trace_id) → full trace object (mandate + policy + rail + state history)
trace.list({ agent_id?, principal_id?, company_id?, since, until }) → trace[]
// 6. Score (read-only, public)
agent.score(agent_id) → { score, tier, last_updated, anchor_tx }All six verbs are exposed identically as MCP tools, REST endpoints, and TypeScript SDK methods. The Agent's MCP endpoint at /mcp/agent is scoped to one row in agent_identities — the agent literally cannot call beyond its own identity tier.
The mandate chain
Intent → Cart → Payment
The Agent Payments Protocol uses ES256-signed JWS tokens to make agent intent cryptographically provable. A complete payment is a chain of three signed mandates: Intent → Cart → Payment. Every signature is verified against the agent's published JWK and audited.
IntentMandate
"I authorize agent X to spend ≤ $Y for purpose Z"
CartMandate
Specific items + merchant + total + intent_id
PaymentMandate
Rail + recipient + cart_id + final amount
{
"iss": "did:agentwallet:ag_01J…", // agent identity
"sub": "[email protected]",
"aud": "https://vendor.example/ap2/v1",
"iat": 1746345600,
"exp": 1746349200, // 1 hour
"jti": "01J9…uuidv7",
"mandate_type": "payment",
"intent_id": "01J9intent…",
"cart_id": "01J9cart…",
"rail": "x402_usdc_base",
"amount": { "value": "125.00", "currency": "USD" },
"recipient": { "address": "0xabc…" }
}
// Header
{ "alg": "ES256", "kid": "ag_01J9…#k1", "typ": "JWS" }MCP & discovery
Per-agent tool catalog.
Each agent has its own MCP endpoint at /mcp/agent, scoped to one row in agent_identities. Drop the URL and the agent's pak_… key into any MCP-compatible client and the agent is online.
| Tool | What it does | Limit-gated? |
|---|---|---|
| wallet.status | Computed balance + holds + headroom | — |
| wallet.send_payment | Initiate fiat payout via available rail | yes |
| wallet.send_usdc | USDC transfer on Base via CDP | yes |
| wallet.x402_pay | Sign EIP-3009 auth for x402 endpoint | yes |
| wallet.list_activity | Recent transactions across sources | — |
| wallet.request_approval | Surface pending action to Principal widget | — |
| card.details | Last4 + balance + status (no PAN) | — |
| card.recent_authorizations | Card-level transactions only | — |
| comms.send_sms | Outbound SMS via telephony provider | — |
| comms.send_email | Outbound email via email provider | — |
| comms.list_inbox | Unread inbound messages | — |
| identity.who_am_i | Agent metadata + AP2 public key | — |
| identity.sign_mandate | ES256 sign an AP2 IntentMandate | — |
| identity.discover | Resolve another agent via .well-known | — |
| approvals.poll | Check status of a pending approval | — |
Discovery · /.well-known/agent.json
The ACP standard defines how an agent publishes its capabilities, rails, and AP2 public keys. Merchants and counterparty agents fetch this before transacting.
{
"agent_id": "did:agentwallet:ag_01J9…",
"name": "Acme Procurement Agent",
"public_keys": [
{ "kid": "ag_01J9…#k1", "alg": "ES256",
"jwk": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…" }
}
],
"endpoints": {
"ap2_inbound": "https://agent.acme.example/ap2/v1/payment",
"a2a": "https://agent.acme.example/a2a/v1"
},
"accepts": ["x402_usdc_base", "spt", "ach"],
"erc8004": { "chainId": 8453, "tokenId": "42" }
}Three external rails. One mandate per payment.
Outbound payments leave through one of three protocol stacks. The choice is decided by the merchant's capability advertisement (or by the rail the agent's policy permits).
x402 over EIP-3009
The merchant returns HTTP 402 Payment Required with a payment offer. The agent signs an EIP-3009 transferWithAuthorization (USDC on Base) for the offered amount and replays the request with the signed authorization in the X-PAYMENT header.
// 1. Agent's first request
GET /api/dataset/v1 HTTP/1.1
Host: data.example
Authorization: Bearer pak_…
// 2. Merchant returns 402 with offer
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"x402": {
"amount": "0.10", "currency": "USDC",
"network": "base",
"recipient": "0xabc…",
"validBefore": 1746349200
}
}
// 3. Agent signs EIP-3009 (CDP-managed key) and replays
GET /api/dataset/v1 HTTP/1.1
Host: data.example
X-PAYMENT: eyJ0eXAiOiJ4NDAyIiwiYWxnIjoi… // signed authShared Payment Tokens (SPT)
For card-rail payments to merchants that accept network-issued single-use tokens. The agent requests an SPT, scoped to a single use and to a specific merchant + amount. The token expires; replay is structurally impossible.
const spt = await issuer.paymentTokens.create({
type: 'card',
card: { vault_alias: card.vaultAlias },
single_use: true,
restrictions: {
amount: cartTotalCents,
merchant: merchantId,
expires_at: Math.floor(Date.now() / 1000) + 300
}
});
// Persist for audit
await db.insert(acpSptIssuances).values({
agentIdentityId, tokenId: spt.id,
cartMandateId, amountCents: cartTotalCents
});Why a protocol — not just an API.
An API is a wrapper around money movement. A protocol is a contract about who moved it, why they were allowed to, and how it can be replayed. Without that contract, agent commerce is unauditable. With it, an Agent can transact with another Agent at any company on any rail, and both sides can prove what happened.