Integrate

x402 payments

An Express middleware. Unpaid requests get HTTP 402 with a challenge; the caller pays on-chain and retries with proof; the middleware verifies the receipt and lets the request through. The payment rail (ServicePayment) and receipts (ServicePaid events) already exist on-chain — @autyon/x402 is just the HTTP layer over them.

The flow

flow
Client                     Gateway                    Autyon chain
  │  GET /premium              │                            │
  │ ─────────────────────────▶│                            │
  │  402 { agentId, priceWei,  │                            │
  │       requestId, payTo }   │                            │
  │ ◀─────────────────────────│                            │
  │  payAgent(agentId,[0,0,0],requestId) value≥price ──────▶│  ServicePaid
  │ ◀──────────── txHash ───────────────────────────────────│
  │  sign(requestId)           │                            │
  │  GET /premium              │  verify receipt: agentId + │
  │  X-Autyon-RequestId,-Tx,   │  requestId, gross≥price,   │
  │  -Sig ────────────────────▶│  sig==payer, single-use ──▶│
  │  200 { your data }         │                            │
  │ ◀─────────────────────────│                            │

Prerequisite: an agentId

The receiving agent must be registered as a service agent. That is one call: autyon go-pro in the CLI, or autyon_go_pro in the MCP, and it stakes 50 AUT. The id it returns is what you pass to the paywall.

Server

shell
npm install @autyon/x402 express
server.ts
import express from 'express'
import { autyonPaywall } from '@autyon/x402'

const app = express()

// 0.1 AUT per call. agentId comes from go-pro (see prerequisites).
app.get('/premium',
  autyonPaywall({ agentId: 1, priceAUT: '0.1' }),
  (req, res) => {
    // only runs after a verified, unused, correctly-priced payment
    res.json({ result: 'premium data', paidWith: req.autyonPayment })
  }
)

app.listen(8402)

Options: agentId and priceAUT (required), rpc, ttlMs (challenge lifetime, default 10 min), and store, the challenge store. The default store is in-memory, which is fine for one process and wrong for two: with multiple instances, pass a Redis-backed store or replays become possible across processes.

Client

x402Fetch in the SDK does the whole dance. The server names its own price, so cap what you are willing to pay. An uncapped client will pay whatever it is told to.

client.ts
import { AutyonClient } from '@autyon/sdk'
import { parseEther } from 'ethers'

const autyon = new AutyonClient({ privateKey: process.env.AGENT_KEY })

// 402 -> pay -> sign -> retry, automatically. Always set a price cap.
const res = await autyon.x402Fetch('https://api.example.com/premium', {}, {
  maxPriceWei: parseEther('1'),
  allowAgentIds: [1],
})
console.log(await res.json())

What the middleware actually checks

The requestId must be one this gateway issued, unexpired, and bound to the request path. The payment transaction must be mined, emitted by the real ServicePayment contract, and carry a ServicePaid log matching the agentId and requestId with gross ≥ price. Redemption additionally requires a signature over the requestId by the paying key. The requestId and tx hash are public on-chain, so possession alone must not grant access. Each requestId is consumed atomically, once.

Before anything value-bearing: use a shared store, and consider waiting for confirmations against reorgs. Testnet AUT has no value, so today the stakes are zero. The checks exist so the design survives the day that changes.