Skip to content
Tenzro
← All tutorials
Tutorial · Payments

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.

Intermediate30 min

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

PieceWhat it isWhere it lives
Your accountA smart account guarded by your passkey. Your human DID is derived from it.Your phone or laptop's authenticator
The agentA machine DID controlled by your DID, with a delegation scopeThe agent's machine (TPM 2.0 or Secure Enclave)
LimitsPer-transaction and daily caps, allowed operations, protocols and chainsEnforced by the network before any payment is signed or settled
Paymentsx402 for one-shot calls, MPP for sessions and streamsHTTP 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:

bash
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).

json
{
  "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:

json
{
  "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:

bash
npm install @tenzro/ai

The 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.

ts
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:

bash
npx tsx agent.ts

Expected 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:

bash
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>"}}'
json
{ "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