Documentation
Build on theopay.xyz
Everything you need to wrap an API, sell per-call access, and let humans or agents pay. The rail follows the Coinbase x402 spec; settlement is USDC on Solana.
Overview
theopay.xyz is a hosted facilitator, proxy, and marketplace for x402 — the open standard for charging per HTTP request. Point us at your existing API and a price; we give you a gated proxy URL. When a buyer (human or agent) calls it, we issue an HTTP 402, verify their on-chain payment, and forward the request to your origin.
Quickstart
Call any gated endpoint with an x402-aware client. It intercepts the 402, signs the payment with your wallet, and retries — your code just sees a normal 200.
import { wrapFetchWithPayment } from "x402-fetch";
import { createSolanaWallet } from "x402-fetch/solana";
const wallet = createSolanaWallet(process.env.AGENT_SOLANA_KEY!);
const fetchWithPay = wrapFetchWithPayment(fetch, wallet);
const res = await fetchWithPay("https://theopay.xyz/x/fetch-x-post?id=1789…");
const data = await res.json(); // → { author: "@levelsio", text: "…", … }The x402 flow
- 1.A client (human or agent) requests a gated endpoint with no payment.
- 2.theopay.xyz responds 402 Payment Required + amount, asset, chain, and payTo.
- 3.The client signs a USDC payment on Solana and re-sends with the X-PAYMENT header.
- 4.theopay.xyz verifies the payment on-chain.
- 5.We forward to your origin and stream back the response.
# 1) No payment → challenge
GET /x/fetch-x-post HTTP/1.1
HTTP/1.1 402 Payment Required
{
"x402": {
"amount": "2000", // 0.002 USDC (6 decimals)
"asset": "USDC",
"chain": "solana",
"payTo": "Theo…Treasury",
"resource": "/x/fetch-x-post"
}
}
# 2) With payment → success
GET /x/fetch-x-post HTTP/1.1
X-PAYMENT: <signed-solana-payment-authorization>
HTTP/1.1 200 OK
{ "author": "@levelsio", "text": "shipped a new feature 🚀", "likes": 4210 }Core concepts
- Listing
- An API you've wrapped: an origin URL + a per-call price. Each listing gets a public slug and a gated proxy URL at /x/{slug}.
- Gated proxy
- The URL we host in front of your origin. It enforces payment, then forwards the request and streams back your origin's response.
- Facilitator
- The component that issues the 402 challenge and verifies the on-chain payment before releasing the response. theopay.xyz runs it for you.
- Payment authorization
- A signed Solana payment the client sends in the X-PAYMENT header. It both pays for and authorizes the request — no account needed.
- Settlement
- USDC moves on Solana per call to your treasury wallet, minus the flat 1% margin. Fast, cheap, deterministic finality.
Authentication
Two audiences, two models. Buyers and agents have no account — calls are authorized by a signed Solana payment in the X-PAYMENT header. Sellers manage listings with a bearer API key.
curl https://theopay.xyz/api/v1/listings \
-H "Authorization: Bearer tm_live_xxxxxxxxxxxx"Gated proxy
Every listing is reachable at a gated proxy path. Call it like the underlying API — we handle payment, then forward your request (method, headers, body) to the origin and stream back the response.
/x/{slug}Call a gated endpoint
Proxies to the seller's origin. Returns 402 until a valid payment header is present, then 200 with the origin's response.
Parameters
slugstringrequiredThe listing's public slug, e.g. fetch-x-post.X-PAYMENTheaderoptionalSigned Solana payment authorization. Omit to receive the 402 challenge.
const res = await fetchWithPay("https://theopay.xyz/x/fetch-x-post");
// 402 → pay → retry happens automatically
const data = await res.json();Listings
Programmatically manage the APIs you sell. All routes require a seller bearer token.
/api/v1/listingsList your listings
Returns all listings owned by the authenticated seller.
/api/v1/listingsCreate a listing
Wrap an origin URL in x402 and get back a gated proxy URL.
Parameters
namestringrequiredDisplay name shown in the marketplace.origin_urlstringrequiredYour real API endpoint to proxy to.price_usdcnumberrequiredPrice charged to the buyer per call, in USDC.categorystringoptionalMarketplace category, e.g. data, ai, finance.
curl -X POST https://theopay.xyz/api/v1/listings \
-H "Authorization: Bearer tm_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Fetch X Post",
"origin_url": "https://api.yoursite.com/v1/x-post",
"price_usdc": 0.002,
"category": "data"
}'
# → { "id": "lst_123", "slug": "fetch-x-post",
# "proxy_url": "https://theopay.xyz/x/fetch-x-post" }/api/v1/listings/{id}Update a listing
Change the price, origin, name, or category of a listing.
Parameters
idstringrequiredThe listing ID.
/api/v1/listings/{id}Delete a listing
Permanently removes a listing and disables its proxy URL.
Parameters
idstringrequiredThe listing ID.
Transactions
Read the per-call payment record for your listings — for earnings, analytics, and reconciliation.
/api/v1/transactionsList transactions
Paginated payment records across your listings, newest first.
Parameters
listing_idstringoptionalFilter to a single listing.limitnumberoptionalPage size (default 50, max 200).cursorstringoptionalPagination cursor from a previous response.
{
"id": "txn_abc123",
"listing_id": "lst_123",
"amount_usdc": 0.002,
"fee_usdc": 0.00002,
"net_usdc": 0.00198,
"chain": "solana",
"signature": "5f…onchainSig",
"status": "settled",
"created_at": "2026-06-15T10:00:00Z"
}Errors
theopay.xyz uses conventional HTTP status codes. The body is always JSON with an error object.
400Bad request — a parameter is missing or malformed.401Missing or invalid seller API key.402Payment required — pay and retry with X-PAYMENT.404Listing or resource not found.429Rate limited — slow down and retry.502The seller's origin returned an error or timed out.Pagination
List endpoints are cursor-paginated. Pass limit (max 200) and the next_cursor from the previous response as cursor to fetch the next page. When next_cursoris null, you've reached the end.
GET /api/v1/transactions?limit=50
{
"data": [ /* … */ ],
"next_cursor": "txn_abc123", // pass as ?cursor= for the next page
"has_more": true
}Rate limits
The management API is limited per API key. Paid proxy calls aren't rate-limited by us beyond your origin's own limits. Every response includes the headers below; on 429, back off and retry after Retry-After seconds.
X-RateLimit-LimitRequests allowed per window.X-RateLimit-RemainingRequests left in the current window.X-RateLimit-ResetUnix time when the window resets.Webhooks
Subscribe to transaction.settled to get a signed POST to your endpoint on every paid call — useful for ledgers, alerts, and provisioning. Verify the X-Theo-Signature header against your webhook secret.
POST https://your-app.com/webhooks/theomarket
X-Theo-Signature: t=1718446800,v1=5f2c…
{
"event": "transaction.settled",
"data": {
"id": "txn_abc123",
"listing_id": "lst_123",
"net_usdc": 0.00198,
"signature": "5f…onchainSig"
}
}SDKs & tools
x402-fetch (TypeScript)
Drop-in fetch wrapper that auto-resolves 402s. The fastest path for JS/TS agents.
REST API
Language-agnostic. Manage listings and read transactions over plain HTTP with a bearer key.
Solana wallet adapters
Phantom, Solflare, Backpack, and embedded wallets all work for signing payments.
Webhooks
Push transaction events to your own infrastructure for ledgers and provisioning.
Testing
Use a Solana devnet wallet and a devnet USDC mint to exercise the full 402 → pay → 200 loop without spending real funds. Point your client at the devnet RPC and fund the wallet from a faucet.
// point your client at Solana devnet
const wallet = createSolanaWallet(DEVNET_KEY, {
rpcUrl: "https://api.devnet.solana.com",
});
// fund it: solana airdrop 1 <address> --url devnet
const res = await fetchWithPay("https://theopay.xyz/x/fetch-x-post");