Private agentic payments.
x402-shaped.
An HTTP layer over MagicBlock’s Private Ephemeral Rollups. Agents pay USDC for APIs. The recipient stays hidden.
$ curl https://api.px402.allensaji.dev/api/sentiment?token=SOL
How it works
The server returns 402 with an HMAC-signed token. The client pays USDC through MagicBlock’s private rail, encrypting the recipient into a TEE box. The server’s subscriber polls the queue PDA, matches the payment by clientRefId, and serves the response on retry. Stateless server, private destination, USDC settlement.
frame-accurate render. base-chain pay → TEE decrypt → queue crank → server retry.
Two lines, two sides.
One middleware on the server. One fetch wrapper on the client. Everything else is the protocol doing its job.
import { Hono } from "hono";import { px402 } from "@px402/hono";import { PrivateTransferSubscriber } from "@px402/core"; const subscriber = new PrivateTransferSubscriber({ rpcUrl, // base RPC queuePda, mint, receiverWallet,});await subscriber.start(); const app = new Hono();app.use(px402({ serverSecret: process.env.PX402_SECRET!, paymentAddress: SERVER_WALLET, pricing: { "/api/sentiment": "10000" }, // micro-USDC subscriber,})); app.get("/api/sentiment", (c) => c.json({ signal: "bullish" }),);pnpm add @px402/hono @px402/core
import express from "express";import { px402 } from "@px402/express";import { PrivateTransferSubscriber } from "@px402/core"; const subscriber = new PrivateTransferSubscriber({ rpcUrl, // base RPC queuePda, mint, receiverWallet,});await subscriber.start(); const app = express();app.use(px402({ serverSecret: process.env.PX402_SECRET!, paymentAddress: SERVER_WALLET, pricing: { "/api/sentiment": "10000" }, // micro-USDC subscriber,})); app.get("/api/sentiment", (_req, res) => res.json({ signal: "bullish" }),);pnpm add @px402/express @px402/core
// app/api/sentiment/route.tsimport { NextResponse } from "next/server";import { withPx402 } from "@px402/next";import { PrivateTransferSubscriber } from "@px402/core"; const subscriber = new PrivateTransferSubscriber({ rpcUrl, // base RPC queuePda, mint, receiverWallet,});await subscriber.start(); export const GET = withPx402( { serverSecret: process.env.PX402_SECRET!, paymentAddress: SERVER_WALLET, pricing: { "/api/sentiment": "10000" }, // micro-USDC subscriber, }, () => NextResponse.json({ signal: "bullish" }),);pnpm add @px402/next @px402/core
import { Px402Client } from "@px402/client"; const client = new Px402Client({ wallet, mint });const res = await client.fetch( "https://api.px402.allensaji.dev/api/sentiment?token=SOL",);const data = await res.json();Six packages. Pick what you need.
Adopters install one adapter for their framework plus the client. The surface stays small on purpose.
on npm: npmjs.com/org/px402 · source: github.com/Allen-Saji/px402
Run a paid call against devnet.
Three priced routes. Mock data, real protocol. Each call settles a USDC payment before the response lands.
# step 1: fetch the route, server returns 402 with payment headers $ curl -i https://api.px402.allensaji.dev/api/sentiment?token=SOL HTTP/1.1 402 Payment Required X-Payment-Amount: 10000 X-Payment-Address: 6dRPtBVYi... X-Payment-Id: 1234567890 X-Payment-Token: v1.eyJ... # step 2: pay + retry through @px402/client (curl can't sign Solana txs) $ pnpm tsx examples/agent --route /api/sentiment
~4s end-to-end. 96.7% success at 30 concurrent.
What an explorer sees.
The transfer is real and on-chain. Three fields are public. Three are hidden inside the TEE.
- Sender wallet
- Mint (USDC)
- Amount (micro-USDC)
- Recipient wallet
- Which API was paid
- Server's revenue mapping
An outside observer cannot tell which API a paying agent consumed.
What is NOT private (and other honest scope).
- Per-call amount is on-chain.
If only one server charges 0.073 USDC for one specific endpoint, the amount itself is a fingerprint.
- Anonymity-set size matters.
At launch, you are alone on the validator. Privacy comes online with volume.
- Not a drop-in x402 facilitator.
Migrating from canonical x402-svm is a rewrite, not a config swap.
- Pre-alpha. Devnet only.
~4s single-call latency. 96.7% success at 30 concurrent. Numbers from a clean devnet run.
- TEE trust assumption.
MagicBlock's TEE has the recipient mapping. If the TEE is compromised, unlinkability is revealed retroactively.
These are honest constraints, not roadmap items. Read the README for the full list.