Developers

Standard x402. Three calls.

One line of middleware, or standard x402 v2 if you already have a stack. Base, Solana and Cardano behind one API and one key. Build in a free test environment, then go live with one setting.

Environments

Test for free. Go live when you're ready.

Every site runs in test or live mode, decided by the tokens it accepts. Same endpoints, same API key, same code: going live is one setting in the dashboard.

TestBase Sepolia · Solana Devnet · Cardano Preprod
Money
Test EURC and USDC from Circle's faucet, test USDM from the tUSDM faucet. Worth nothing.
Cost
Free. No credits needed, up to 1,000 settlements per account per day.
Turn it on
Select test tokens in the site's settings. A site with only test tokens shows a test mode label.
Try it
Press Pay with test tokens on the site page to pay a path from your browser wallet (MetaMask, Phantom, Lace…), exactly as an agent would.
Numbers
On the Test tab of the site's analytics, never on your bill.
LiveBase · Solana · Cardano
Money
Real EURC, USDC or USDM, straight to your wallet.
Cost
€0.002 per successful settlement, from prepaid credits.
Turn it on
Select mainnet tokens in the site's settings and top up credits.
Safety net
Without credits, verify answers 402 and your server doesn't serve paid content it can't settle.
Numbers
On the Live tab of the site's analytics and on the monthly bill.

Going live

  1. In the site's settings, set a wallet for each chain, select the mainnet tokens you want and deselect the test tokens.
  2. Top up credits on the Billing page.
  3. That's it. Your integration picks up the new accepts list within a minute; check that the config API reports "mode": "live".

Want to keep testing after launch? Add a second site, for example staging.example.eu, that stays in test mode with its own API key. A site can also accept both at once ("mode": "mixed"); payers then choose the network.

Quickstart

One line in your server.

  1. Create a site in the dashboard, select a test token to start in test mode, and copy its site ID and API key. Keep the key on your server.
  2. npm install @pennipay/x402 and add the middleware for your framework.
  3. Set prices per path in the dashboard. Requests without payment get a 402; paid ones reach your handler with the payment attached.

Express, Hono, Next.js, Fastify, and a fetch wrapper for Cloudflare Workers, Bun and Deno. No runtime dependencies. On WordPress or without code: see integrations.

import express from "express";
import { penni } from "@pennipay/x402/express";

const app = express();
app.use(penni({ siteId: process.env.PENNI_SITE_ID, apiKey: process.env.PENNI_API_KEY }));

// Priced in the dashboard: /premium/** at €0.05
app.get("/premium/report", (req, res) =>
  res.json({ report: "…", payer: res.locals.payment?.payer }));

The middleware caches prices per path for a minute, checks every payment against your current price before it reaches us, settles before your handler runs (or after a successful response, if you prefer), and handles Cardano's wait for confirmations. Already using x402 middleware? Point it at the facilitator URL https://api.pennipay.eu/v1/x402 with your site key as Bearer token.

API reference

Endpoints.

All calls go to https://api.pennipay.eu, version 1 of Penni's public API. Calls that need a key authenticate with Authorization: Bearer <site key>. Bodies are JSON. The x402 facilitator URL is https://api.pennipay.eu/v1/x402: standard x402 clients add /verify, /settle and /supported to it.

EndpointWhat it does
GET /v1/sites/:id/config?path=Price and ready-made x402 accepts list for one path. Without path: every price rule. Includes mode (test, live or mixed), ready and problems (no wallet, no tokens, out of credits). Cacheable for 60 seconds, supports ETags.
GET /v1/x402/supportedSchemes, networks and tokens the facilitator handles, and its extensions (bazaar). Public.
POST /v1/x402/verifyChecks a signed payment without moving money. Body: { x402Version, paymentPayload, paymentRequirements }. Free.
POST /v1/x402/settleSubmits the payment on-chain and returns the transaction hash. Same body. Costs €0.002 in credits when it succeeds. On Cardano it can answer settlement_pending: send the same body again.
GET /v1/x402/discovery/resourcesThe public x402 Bazaar catalog of resources from sites that opted in. Public, paginated, filterable by network and wallet. /v1/x402/discovery/search?query= searches it.
GET /v1/x402/discovery/price?url=What one URL of an opted-in site costs, with the same accepts its 402 carries. Public.
GET /v1/healthAnswers { "ok": true } while the API is up. Public.

Versioning: new fields and endpoints are added within /v1, so ignore fields you don't know. A breaking change ships as /v2, and /v1 keeps working next to it until we announce its end well in advance.

Errors.

Penni's own checks answer with an HTTP status and { "error": "…" }. Results from the facilitator itself (isValid, success, reasons) pass through unchanged.

StatusMeaning
400The body is not valid JSON, or paymentRequirements is missing.
401Missing or invalid site key, or a key for another site.
402Out of credits. Top up in the dashboard; no settlement has been attempted. Never for testnet payments.
403Site paused, payTo is not the site's wallet, or the token is not accepted for this site.
409This Solana transaction was already settled; a payment counts once.
429Daily limit of free test settlements reached. Resets at 00:00 UTC.
502 / 503The facilitator could not be reached, or this server doesn't serve that chain. Safe to retry verify; for settle, check the transaction first.
Networks

Tokens per environment.

EURC and USDC are issued by Circle; USDM on Cardano by Moneta. On Base and Solana we pay the network fee, so payers only need the token. On Cardano the payer pays it in ADA. Build on the test networks: test settlements are free, up to 1,000 a day. Your code doesn't change when you go live.

NetworkModeTokenContract
Base · eip155:8453LiveEURC0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42
Base · eip155:8453LiveUSDC0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Base Sepolia · eip155:84532TestEURC0x808456652fdb597867f38412077A9182bf77359F
Base Sepolia · eip155:84532TestUSDC0x036CbD53842c5426634e7929541eC2318f3dCF7e
Solana · solana:5eykt4…LiveEURCHzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr
Solana · solana:5eykt4…LiveUSDCEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
Solana Devnet · solana:EtWTRA…TestEURCHzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr
Solana Devnet · solana:EtWTRA…TestUSDC4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU
Cardano · cardano:mainnetLiveUSDMc48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d
Cardano Preprod · cardano:preprodTestUSDMe675b46e4d2242c991a8932a99db3044e80515ae14b4c4ccf6b3f4c9.0014df10745553444d
Integrations

Bring your own stack.

No code

WordPress plugin

Charge AI agents for your posts while readers keep reading. Install, paste your key, done.

No code

Cloudflare Worker

Put Penni in front of any site or API, wherever it's hosted, with a Worker you deploy in minutes.

Any stack

Plain HTTP

Python, Go, PHP: fetch the price from the config API, answer 402, then verify and settle with the endpoints above. Or point existing x402 middleware at https://api.pennipay.eu/v1/x402.

Ship the paywall today.

Get a site key