INVADERS
Developers

How x402 works

The HTTP 402 payment standard that settles a paid AI call in USDC, leg by leg, and how this app drives it from a delegated wallet.

x402 is how a paid AI suggestion gets paid for. It is a small, open standard that revives the long-dormant HTTP 402 Payment Required status code: the server quotes a price in a 402 response, the caller pays on-chain, and the caller retries the same request carrying a proof of payment. No invoices, no API keys, no accounts. The price and the payment travel inside the request the AI provider already answers.

This page is for developers. If you only want to know what a paid call costs you and when, read AI suggestions and Fees instead. Here we trace the protocol leg by leg and name the code that drives it.

The shape of the exchange

A normal HTTP call is one round trip: request, response. x402 inserts a payment in the middle, so a paid call is two legs against the same endpoint:

  1. The quote. The caller POSTs the request. The provider does not answer with data. It answers 402 Payment Required and a body that lists what it will accept: the network, the price, and the address to pay.
  2. The payment and retry. The caller signs an on-chain payment for that price, encodes it into a header, and sends the same request again. This time the provider verifies the payment, settles it, and answers 200 with the result.

The price is never hardcoded on our side. It is read live from the provider's 402, so if the provider reprices, the next call simply pays the new amount.

client ──POST suggestion──▶ provider
client ◀──402 + accepts[]── provider     leg 1: the quote (price, network, payTo)
        sign EIP-3009 USDC authorization
client ──POST + X-PAYMENT─▶ provider
client ◀──200 + suggestion─ provider     leg 2: pay, settle, answer

What the 402 says

The 402 body is the offer. It follows the standard x402 shape — a version, plus an accepts array of offers the caller can choose from:

{
  "x402Version": 1,
  "accepts": [
    {
      "scheme": "exact",
      "network": "base",
      "maxAmountRequired": "25000",   // atomic USDC, 6 decimals → $0.025
      "payTo": "0x…",                 // the recipient address
      "resource": "https://…/x402/perp-trading-suggestion"
    }
  ]
}

Three fields drive the payment:

  • network is the chain to settle on. This app accepts the base offer; the perp-suggestion endpoint settles only on Base (eip155:8453), in USDC.
  • maxAmountRequired is the price as an atomic integer string — USDC has 6 decimals, so 25000 means $0.025. We convert it to decimal dollars before comparing it against your Agent Balance.
  • payTo is the only address the payment may go to. It is fixed by the offer, not chosen by us.

The payment itself: EIP-3009

The payment is not a normal token transfer. The exact scheme signs an EIP-3009 TransferWithAuthorization — a USDC primitive that lets the holder sign a transfer off-chain, which a third party then submits on-chain. That is what makes the retry possible: the caller produces a signature, encodes it into the X-PAYMENT header, and the provider (or its facilitator) submits and settles it.

Because it is a signature over an exact amount to an exact recipient, it cannot be replayed for more, redirected, or topped up. The signed authorization is the payment proof.

In this app that signature does not come from a key we hold. It comes from a delegated signer backed by the user's Agent Wallet inside Privy, scoped by a delegation the user granted: one action (usdc-transfer-with-authorization), one recipient (the AI provider), a spend cap, and an expiry. The protocol asks for a signature; the delegation decides whether one is allowed. See Self-custody for the exact boundary.

How the app drives it

We do not hand-build payment headers. The whole loop is driven by the official x402 SDK, in the minara-client adapter — the one place in the server that speaks x402.

// apps/server/src/minara-client/minara-client.ts
import { registerExactEvmScheme } from '@x402/evm/exact/client';
import { wrapFetchWithPayment, x402Client } from '@x402/fetch';

// Register the EIP-3009 "exact" scheme with the user's delegated signer,
// then let the SDK turn one fetch into the full 402 → sign → retry loop.
const client = registerExactEvmScheme(new x402Client(), { signer });
const paidFetch = wrapFetchWithPayment(boundedFetch, client);
const response = await paidFetch(suggestionUrl, requestInit);

wrapFetchWithPayment is the trick: it returns a fetch that, on a 402, reads the offer, asks the registered scheme to sign for the chosen network, attaches the payment header, and retries — all transparently, so the calling code looks like one request. The signature is produced by the SDK from the injected signer; we never assemble the proof by hand.

The adapter exposes two entry points over this machinery:

  • quotePrice runs only the first leg. It sends the request, expects a 402, and reads maxAmountRequired + payTo out of accepts[0]. This is how the engine prices a call and pre-flights your Agent Balance before anything is signed.
  • payAndCall runs the full loop and returns the settled suggestion.

Outcomes, and what they mean

ResultWhat happened
200 with a suggestionPaid and settled. Exactly one charge, recorded with the suggestion.
Still 402 on the retryThe payment was rejected (e.g. expired authorization). Surfaces as PaymentRejected; nothing settles.
Any other non-2xx, or unreachableTreated as an upstream failure (MinaraCallFailed); the provider's error body is logged server-side, never returned on the wire.

The whole two-leg round trip is bounded by a single 90-second deadline, so a hung provider cannot leave a payment in limbo — both legs share one clock.

Why this design

  • No keys, no accounts. A provider does not need to know who you are or issue you a credential. It quotes a price and accepts a signed USDC authorization. That is the entire relationship.
  • Pay-per-call, exactly. The amount is fixed by the offer and signed exactly, so a call costs what the provider quoted — no subscription, no minimum, no overage.
  • Self-custodial by construction. Because the payment is an EIP-3009 signature from a scoped delegated wallet, the most a compromised server could do is pay the one approved provider up to your cap. It can never reach your trading collateral or redirect funds.

Under the hood

  • The x402 integration is apps/server/src/minara-client/. It is the only module that imports @x402/fetch and @x402/evm. It reads the price from the live 402, drives the paid retry, and maps the outcomes above.
  • The signer and the spend rules are in apps/server/src/agent-treasury/ — the delegated signer that produces the EIP-3009 signature, and the delegation that bounds it to one action, one recipient, a cap, and an expiry.
  • The engine that decides to pay is apps/server/src/suggestions/. It validates before paying and caches results, so most repeat questions never reach a 402 at all.

Design records

x402 payments through a self-custodial delegated wallet (ADR-0044), the paid path's request timeout and single shared deadline (ADR-0073), and the Minara provider contract that the paid call settles (ADR-0048).