Build an AI payment agent
Give an AI agent its own delegated wallet with spending limits, fund it with TNZO or stablecoins, and let it pay for inference over x402 and MPP.
In this tutorial you build an agent that pays for its own inference. You create a passkey wallet in the console, create a delegated agent wallet with limits you control, fund it with TNZO or stablecoins, and write a small TypeScript agent that answers HTTP 402 payment challenges over x402 and MPP. No private key is ever copied, pasted or stored in a file: your account is rooted in a passkey, and the agent's key lives in the hardware of the machine that runs it.
How the pieces fit
| Piece | What it is | Where it lives |
|---|---|---|
| Your account | A smart account guarded by your passkey. Your human DID is derived from it. | Your phone or laptop's authenticator |
| The agent | A machine DID controlled by your DID, with a delegation scope | The agent's machine (TPM 2.0 or Secure Enclave) |
| Limits | Per-transaction and daily caps, allowed operations, protocols and chains | Enforced by the network before any payment is signed or settled |
| Payments | x402 for one-shot calls, MPP for sessions and streams | HTTP 402 challenges on the provider's route |
Prerequisites
- A browser and device that support passkeys.
- Node.js 20 or newer on the machine that will run the agent, with a TPM 2.0 or Secure Enclave.
- Background: Console and passkey wallet and Payments.
1. Create your passkey wallet
Open the console wallet and create a wallet. Your device creates a passkey with user verification; every operation it authorises is signed with a hybrid P-256 and ML-DSA-65 signature. The console shows your DID and your account address.
Then link a second device or set up recovery guardians from the same page. A second root means that losing one device never locks you out. See Device linking and recovery.
2. Fund your account
You need TNZO for network fees and, if you want the agent to pay in stablecoins, a stablecoin balance.
- TNZO in the account: fund it by transferring TNZO to its address from another wallet or account.
- Stablecoins: add a stablecoin wallet from the console wallet page. Stablecoin wallets are provided through Bridge.xyz; you deposit USDC or another supported stablecoin to it and move funds to your agents from there. See Stablecoin payments.
Check the TNZO balance from any terminal:
curl -s https://rpc.tenzro.xyz \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_getBalance","params":["0x<your-account>","latest"]}'3. Pair the agent's machine
On the machine that will run the agent, start the Tenzro agent runtime (the CLI, the SDK or the desktop app). It creates the agent's key inside the TPM or Secure Enclave and shows a pairing payload: the device public key and a machine id, as text or a QR code. The key is hardware-rooted, and the console only ever sees the public half.
4. Create a delegated agent with limits
Open Agents, choose "New agent", and paste or scan the pairing payload. Set the delegation scope. Amounts are in the smallest unit of the asset (wei for TNZO).
{
"max_transaction_value": "2000000000000000000",
"max_daily_spend": "20000000000000000000",
"allowed_operations": ["inference", "transfer"],
"allowed_payment_protocols": ["x402", "mpp"],
"allowed_chains": ["tenzro"]
}Confirm with your passkey. Creating an agent, and every later change to its limits, is an owner action signed by your passkey; a session token alone can never change them. The console returns the agent's identity:
{
"identity": {
"did": "did:tenzro:machine:<your-id>:<agent-id>",
"identity_type": "machine",
"controller_did": "did:tenzro:human:<your-id>",
"capabilities": ["inference", "payments"],
"status": "active"
},
"wallet": { "address": "0x<agent-wallet>" }
}The agent runtime receives its own access token, bound to its hardware key. Do not copy it anywhere else.
5. Fund the agent
From the console, send the agent a small TNZO balance and, if you use stablecoins, a stablecoin allowance from your Bridge.xyz wallet. Keep the agent's balance close to what it needs: the limits cap each payment and each day, and the balance caps the total.
6. Write the agent
Install the AI SDK:
npm install @tenzro/aiThe SDK signs each request with a Signer, answers the provider's 402 Payment Required challenge with x402 or MPP, retries, and returns the receipts. Implement the Signer over the agent's hardware key; the runtime's key binding returns the hybrid signature.
import {
generateText,
tenzro,
DelegationViolationError,
PaymentRequiredError,
type Signer,
} from "@tenzro/ai";
import { hardwareKey } from "./hardware-key"; // your runtime's TPM or Secure Enclave binding
const AGENT_DID = "did:tenzro:machine:<your-id>:<agent-id>";
const signer: Signer = {
did: () => AGENT_DID as ReturnType<Signer["did"]>,
// Returns { classical, postQuantum } signed inside the hardware.
sign: (preimage) => hardwareKey.signHybrid(preimage),
};
try {
const { text, receipts } = await generateText({
model: tenzro("qwen3.6-35b-a3b"),
prompt: "Summarise today's GPU rental prices in two sentences.",
signer,
payment: {
protocol: "auto", // x402 for one-shot calls, MPP for streams
maxPrice: { amount: 50_000n, currency: "USDC" }, // smallest unit of the asset
},
});
console.log(text);
console.log(receipts.payment); // protocol, amount, currency, providerDid, settledAt
} catch (err) {
if (err instanceof DelegationViolationError) {
console.error("blocked by your limits:", err.violation.kind, err.violation.detail);
} else if (err instanceof PaymentRequiredError) {
console.error("provider asked more than maxPrice or payment failed:", err.message);
} else {
throw err;
}
}maxPrice is a per-call ceiling on top of the delegation scope. If the chosen provider quotes above it, the SDK fails over to another provider. Use currency: "TNZO" to pay in TNZO instead.
Run it:
npx tsx agent.tsExpected output (abridged):
Rental prices for workstation GPUs held steady today...
{ protocol: 'x402', amount: 31200n, currency: 'USDC', providerDid: 'did:tenzro:machine:...', settledAt: 1791100000000 }7. Watch what the agent spends
The agent's daily spend is readable by anyone who knows its DID:
curl -s https://rpc.tenzro.xyz \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tenzro_getAgentDailySpend","params":{"agent_did":"did:tenzro:machine:<your-id>:<agent-id>"}}'{ "agent_did": "did:tenzro:machine:...", "max_daily_spend": "20000000000000000000", "current_daily_spend": "...", "remaining": "...", "last_reset": "..." }When the agent hits a limit, the payment is refused before anything is signed, and the SDK throws a DelegationViolationError whose kind is one of over-transaction-limit, over-daily-limit, operation-not-allowed, protocol-not-allowed, chain-not-allowed or expired. Change the limit or revoke the agent with a fresh passkey approval; see Agents.
8. Stream long responses over a channel
For long streams billed per token, MPP opens a session for the stream and closes it at the end. For an agent that talks to one provider all day, a payment channel is cheaper: funds are locked once, the provider sends signed balance updates as tokens arrive, and only the final state settles on the ledger. Pass protocol: "channel" and the channel id to use one.
Next steps
- Let the agent shop with mandates and ACP checkouts: Agentic commerce workflows.
- The wire protocols in detail: Payments with x402 and Payments with MPP.
- Pay without an agent framework: Pay for inference in stablecoins.
- Limits on your own account's session keys: Smart account policies.