x402 HTTPtslive · you pay per call

Protect an Express agent with x402

A minimal Express server whose /ask endpoint is gated by the Nevermined payment middleware — one line per route.

http-simple-agent-ts/
ExpressTypeScript@nevermined-io/payments/expressx402 + MPPOpen-Meteo
1 What you'll learn

The smallest possible paid HTTP agent: add a paywall to one route.

  • Protect an Express endpoint with paymentMiddleware from @nevermined-io/payments/express
  • Return 402 with the payment-required envelope, verify the payment-signature, settle on success
  • Generate an x402 access token on the client with the SDK and retry the call
  • Optionally track OpenAI cost per call with Nevermined observability
2 How it works

The client calls /ask with no token and gets 402 Payment Required, carrying the payment-required header. It mints an x402 token with the SDK, retries with the payment-signature header, and the agent verifies, runs, and settles (burns credits) before returning 200 with a payment-response receipt.

POST /askno token
402payment-required
sign + retrypayment-signature
200payment-response
3 Under the hood
3.1 Client

Create a delegation once, mint an x402 token, and call the paid route.

# buy the weather over x402
// one delegation backs the buyer (erc4337)
const { delegationId } = await payments.delegation.createDelegation({
  provider: 'erc4337', spendingLimitCents: 10000, durationSecs: 604800, currency: 'usdc',
})

// mint an x402 access token for the plan, then call the route with it
const { accessToken } = await payments.x402.getX402AccessToken(
  PLAN_ID, undefined, { delegationConfig: { delegationId } },
)
const res = await fetch(AGENT_URL + '/weather/credits', {
  method: 'POST',
  headers: { 'content-type': 'application/json', [X402_HEADERS.PAYMENT_SIGNATURE]: accessToken },
  body: JSON.stringify({ city: 'Lisbon' }),
})
// → 200 + weather, plus a 'payment-response' settlement receipt
3.2 Agent

One paymentMiddleware, three plans, both protocols — mpp: true makes every 402 advertise x402 AND MPP, so either buyer works against the same URL.

# three dual-protocol routes gate the agent
import { paymentMiddleware } from '@nevermined-io/payments/express'

app.use(paymentMiddleware(payments, {
  'POST /weather/credits':      { planId: PLAN_ID_CREDITS, credits: 1, mpp: true },
  'POST /weather/subscription': { planId: PLAN_ID_TIME,    credits: 1, mpp: true },
  'POST /weather/payg':         { planId: PLAN_ID_PAYG, credits: (req) => priceForRequest(req.body), mpp: true },
}))

app.post('/weather/credits', async (req, res) => {
  const { city } = parseWeatherRequest(req.body)  // 400 on bad input
  res.json(await getTodayWeather(city))           // keyless Open-Meteo
})
src/agent.tsExpress agent — three dual-protocol (x402 + MPP) weather routes
src/services/weather.service.tskeyless weather from Open-Meteo
scripts/smoke.tsbuyer: x402 + MPP round-trips against the agent
scripts/register-plans.tsregisters the credits / time / pay-as-you-go plans
4 See it run
Protect an Express agent with x402

Sandbox agent — real payment round-trips (402 → authorize → settle) with a per-session credit balance, no external service and no real money. The buyer mints an x402 access token via the SDK and sends it as `payment-signature`; the sandbox runs that round-trip locally. See the README to point this at a hosted instance of the agent.