Permit402 docs
Charge AI agents per call for your API or MCP server. Agents pay in USDC using the x402 standard, and the money goes straight to your wallet.
Want to see it first? Try the two-minute live demo.
Overview
Your API keeps working exactly as it does today. Permit402 adds a price to the endpoints or tools you choose. When an agent calls one, it's asked to pay; once it has paid, the call goes through. The agent is only charged if your API succeeds.
There are two ways to run the toll:
Hosted gateway Available now
No code. We give you a gateway URL that sits in front of your API.
- Set up in about ten minutes
- One small change to your API: check a header
- Agent traffic passes through Permit402
In your app Coming soon
Install a small Permit402 package in your server instead.
- Traffic never leaves your servers
- Same dashboard, prices and stats
- Node (Express, Hono) and Cloudflare Workers first
The rest of these docs cover the hosted gateway.
How it works
You don't need to know anything about crypto to use Permit402. Here are the pieces, in plain English.
| Agent | Software acting for someone, such as an AI assistant, a coding agent or an automated workflow. It calls your API the way any program would. |
| HTTP 402 | "Payment Required". A web status code reserved for payments since the 1990s and finally put to use. The gateway answers a paid request with 402 and a price. |
| x402 | The open standard, started by Coinbase, that says how a 402 describes a price and how an agent sends the payment back. Agents that support it pay automatically, with no sign-up and no API key. |
| USDC | A digital US dollar: one USDC is designed to be worth one US dollar. It's what agents pay with, so your prices are in dollars. |
| Base | The payment network USDC moves on. It's built by Coinbase, and payments settle in seconds for a fraction of a cent. Base Sepolia is its test version, where the USDC is free and worthless. |
| Wallet | Your account on Base, identified by an address starting 0x. Payments land there, and you can move them to Coinbase and convert to pounds, euros or dollars. |
| Facilitator | A service that checks each payment and puts it on the network. Permit402 uses Coinbase's for live payments, and the public x402.org facilitator on the test network. Neither holds the money: the payment goes from the agent's wallet straight to yours. |
| Gateway | Your Permit402 address, e.g. permit402.com/g/forecast-co. It knows your prices, asks agents to pay, and passes paid calls on to your API. |
One paid call, step by step
Agent ──GET /v1/forecast──▶ Gateway Your API
Agent ◀──402: "$0.01 USDC to 0xYou"── Gateway
Agent ──same request + signed payment──▶ Gateway
Gateway ──checks payment (Coinbase)
Gateway ──request + secret header──▶ Your API
Gateway ◀──────── 200 OK + data ──── Your API
Gateway ──settles: $0.01 to 0xYou
Agent ◀──200 OK + data + receipt── Gateway
If your API returns an error, the last step never happens and the agent keeps its money.
What you need
| An API or MCP server | Reachable on the public internet over https://. It can be anything: Node, Python, Go, a serverless function. |
| A wallet address on Base | Where your earnings go. It starts with 0x. The easiest way is the Coinbase app: Receive → USDC → Base network, then copy the address. Any Ethereum wallet works, such as MetaMask or Rabby. We never hold your money and never need your keys. |
| Ten minutes | And the ability to add one small check to your API's code or config. |
Quick start
- Create an account. Open the dashboard and press Create account. You'll be shown an admin key beginning
tk_. That key is your login, and it's only shown once, so save it in your password manager. - Create a gateway. Press New gateway and fill in:
Name Anything you like, e.g. "Forecast API". What's behind it An HTTP API or An MCP server. Your API's URL The base URL of your API, e.g. https://api.yourcompany.com.Your wallet Your 0x…address on Base.Short description Optional. One sentence that agents read, e.g. "7-day forecasts for any UK town". List in the directory Optional. Shows your gateway in the public Permit402 directory once it's verified. Requests with no price Block them (the default) or Pass through free. See Setting prices. Gateway name in the URL Optional. 3–40 lowercase letters, digits and dashes, e.g. forecast-co. - Add prices. For example
GET /v1/forecastat$0.01. Details in Setting prices. - Protect your API. Copy the secret from the gateway's checklist and make your API reject requests without it. Code in Protecting your API.
- Test it with free test USDC. New gateways start on the Base Sepolia test network. See Testing.
- Verify and go live. Prove you own the API, then switch to real USDC. See Going live.
Then give agents your gateway URL instead of your API's URL:
https://permit402.com/g/forecast-co/v1/forecast?city=York
└── gateway ─┘└──── same path and query as your API ────┘
→ forwarded to https://api.yourcompany.com/v1/forecast?city=York
Setting prices
Each price has a method, a path pattern and an amount. When a request comes in, prices are checked top to bottom and the first match wins, so put specific rules above general ones.
| Method | Pattern | Price | Matches |
|---|---|---|---|
GET | /v1/forecast | $0.01 | GET /v1/forecast?city=York |
POST | /v1/reports/* | $0.25 | POST /v1/reports/annual, POST /v1/reports/2026/q3 |
* | /v1/premium/* | $0.05 | Any method under /v1/premium/ |
- Wildcards:
*matches anything, including slashes. - Forgiving matching: case and a trailing slash don't matter, so
/V1/Forecast/still pays the/v1/forecastprice.HEADrequests are priced likeGET. - The query string is ignored for matching but passed on to your API.
- Amounts are in US dollars of USDC, written like
$0.01, from$0.001to$100. Up to 100 prices per gateway.
Requests that match no price
Block them returns 404, so only the paths you price are reachable through the gateway. Pass through free forwards them to your API at no charge, which is useful for health checks, docs or free tiers.
Protecting your API
Agents could skip the toll by calling your API's own URL directly. To prevent that, every request from your gateway carries a secret header:
x-permit402-secret: <your gateway's secret>
Make your API refuse any request that doesn't carry it. Find the secret in the dashboard, under your gateway's Set-up checklist, and keep it in an environment variable. If it ever leaks, press Rotate and update your API.
import express from "express";
const app = express();
app.use((req, res, next) => {
if (req.get("x-permit402-secret") !== process.env.PERMIT402_SECRET) {
return res.status(403).json({ error: "Use the Permit402 gateway" });
}
next();
});
app.get("/v1/forecast", (req, res) => {
const payer = req.get("x-permit402-payer"); // wallet that paid, on paid calls
res.json({ city: req.query.city, days: [/* … */] });
});
import { Hono } from "hono";
const app = new Hono<{ Bindings: { PERMIT402_SECRET: string } }>();
app.use("*", async (c, next) => {
if (c.req.header("x-permit402-secret") !== c.env.PERMIT402_SECRET) {
return c.json({ error: "Use the Permit402 gateway" }, 403);
}
await next();
});
export default app;
// middleware.ts (applies to /api/*)
import { NextResponse, type NextRequest } from "next/server";
export function middleware(req: NextRequest) {
if (req.headers.get("x-permit402-secret") !== process.env.PERMIT402_SECRET) {
return NextResponse.json({ error: "Use the Permit402 gateway" }, { status: 403 });
}
return NextResponse.next();
}
export const config = { matcher: "/api/:path*" };
import os
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
SECRET = os.environ["PERMIT402_SECRET"]
@app.middleware("http")
async def require_gateway(request: Request, call_next):
if request.headers.get("x-permit402-secret") != SECRET:
return JSONResponse({"error": "Use the Permit402 gateway"}, status_code=403)
return await call_next(request)
# inside your server { } block
location /v1/ {
if ($http_x_permit402_secret != "YOUR_SECRET") {
return 403;
}
proxy_pass http://127.0.0.1:3000;
}
If some of your API should stay open to everyone (say, your website), apply the check only to the paths you sell through the gateway.
Using your own domain
Instead of permit402.com/g/forecast-co, agents can use an address on your own domain, such as https://mcp.permit.yourcompany.com. It behaves exactly the same. Developers and agents tend to trust your name more, and if you ever leave Permit402 you just point the record elsewhere and nothing breaks for them.
- On your gateway's page, under Your own domain, enter the address you want and press Connect. Use a subdomain, not your bare domain. It also can't be the same address as your API: if your API is
mcp.yourcompany.com, use something likemcp.permit.yourcompany.com. - Add the CNAME record the dashboard shows you, wherever you manage your domain's DNS:
Type Name Target CNAME mcp.permit customers.permit402.com
If your DNS is on Cloudflare, set the record to DNS only (grey cloud). - Wait a few minutes. Cloudflare checks the record and issues the HTTPS certificate automatically. Press Check now, or reopen the page. When it shows Live, your domain becomes the gateway URL everywhere: the dashboard,
llms.txt, the directory and the server.json for the MCP Registry.
Your permit402.com/g/… address keeps working as well. A domain that isn't connected within 3 days is released, so nobody can hold on to an address they don't control.
Testing
New gateways run on Base Sepolia, a test network where USDC is free and worthless. Nothing real is charged until you switch to live.
- Open the live demo. It creates a test wallet in your browser.
- Get free test USDC from faucet.circle.com: choose USDC and Base Sepolia, then paste the demo wallet's address.
- Paste your gateway URL into the demo's address box, e.g.
https://permit402.com/g/forecast-co/v1/forecast?city=York, and press Pay and call. - You should see your API's response, a view payment link, and the payment in your dashboard a few seconds later.
From code, use any x402 client. With the official SDK:
npm i @x402/fetch @x402/evm viem
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.TEST_WALLET_KEY);
const pay = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [{ network: "eip155:84532", client: new ExactEvmScheme(account) }],
});
const res = await pay("https://permit402.com/g/forecast-co/v1/forecast?city=York");
GET requests. For other methods use code like the above. x402 clients refuse payments over $1 by default; raise their spending limit if you charge more.Going live
1. Prove you own the API
So that nobody can sell access to an API that isn't theirs, you verify each gateway once. The checklist shows a token and a URL on your API's domain:
https://api.yourcompany.com/.well-known/permit402.txt
Serve the token as plain text at that URL, either as a static file or a one-line route, then press Verify. Redirects to another domain aren't followed. If you later move the gateway to an API on a different domain, you'll need to verify again.
2. Switch to live
Once verified, press Switch to live. From then on agents pay real USDC on Base, straight to your wallet.
How agents find you
An agent can only pay for your API if it knows the API exists. There's no single standard for this yet, so Permit402 covers the main routes for you and makes the rest a copy-and-paste job.
Done for you automatically
<gateway>/llms.txt | A plain-text summary written for AI: what you sell, your prices and how to pay. It follows the llms.txt proposal, which many AI tools read. |
<gateway>/.well-known/x402 | The same price list as JSON, for programs. |
| x402 Bazaar | Every 402 your gateway sends describes the endpoint in the x402 discovery format. Once you're live, Coinbase's facilitator can list paid endpoints in its Bazaar directory, which agents with wallets search. |
| Permit402 directory | If you tick List in the directory, your gateway appears on our public directory page, at permit402.com/directory.json and in permit402.com/llms.txt. Only verified gateways appear, so nobody can list an API that isn't theirs. |
Point to it from your own website
Agents often start at your main website. Add a short section to /llms.txt on your site, creating the file if you don't have one, so they find your paid API instead of scraping pages. The dashboard writes this for you under Help agents find you:
## For AI agents - [Forecast API](https://permit402.com/g/forecast-co/llms.txt): 7-day forecasts for any UK town. Pay per call with x402 (USDC).
Also mention it wherever developers look: your API docs, your OpenAPI file and your developer portal. Give agents the gateway URL, never your API's own URL.
Listing in the MCP Registry
If you sell an MCP server, list it in the official MCP Registry. It's the public catalogue that MCP marketplaces and AI apps pull from, backed by Anthropic, GitHub, Microsoft and others. It's still in preview, so its details may change. It stores only a description and your server's URL, not your code.
- Install the publisher tool. On a Mac:
brew install mcp-publisher. For other systems, see the registry's quick start. - Copy your
server.jsonfrom the dashboard (Help agents find you) and replaceYOUR-GITHUB-USERNAME. It points the registry at your gateway URL, so agents that find you there pay as normal:{ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", "name": "io.github.your-username/forecast-co", "title": "Forecast Co", "description": "7-day forecasts for any UK town, paid per call with x402", "version": "1.0.0", "remotes": [ { "type": "streamable-http", "url": "https://permit402.com/g/forecast-co" } ] } - Sign in:
mcp-publisher login github. This proves you own theio.github.your-username/name. The registry can also verify your own domain instead (names likecom.yourcompany/…); see its authentication guide. - Publish:
mcp-publisher publish. Each time you change the file, raise itsversionand publish again.
People vs agents
Permit402 doesn't try to tell people and bots apart. It doesn't need to, because it works with two front doors:
People
Use your website and apps exactly as today. Nothing changes and nobody is charged.
Agents
Use your gateway URL, which you advertise as described above, and pay per call.
For an API or MCP server, that's all you need: only software calls them, and the secret header makes sure that software comes through the gateway.
When bots scrape your website instead
Some bots will ignore your signposts and read your normal web pages. If you want them to use your paid API instead, your website has to recognise them and answer differently. These are the ways to recognise a bot, strongest first:
| Signed requests | Some agents cryptographically sign their requests (the emerging Web Bot Auth standard). These can't be faked. Your CDN or bot-protection service checks them for you. |
| Published addresses | Major AI companies publish the IP ranges their crawlers use. A request from those ranges really is that company's bot. |
| User agent | Honest bots name themselves, e.g. GPTBot, ChatGPT-User, ClaudeBot, Claude-User, PerplexityBot, CCBot. This is easy to fake, so treat it as a hint and check each company's published list. |
| Behaviour | Speed, volume and missing browser features. This catches hidden scrapers but is never certain. Use it to show a challenge, not to charge. |
Bots that identify themselves are the ones that will pay, so point those to your gateway. Bots that hide won't pay anyway, so block or challenge them. Here's a simple example for an Express website that sends recognised AI bots to your paid API instead of serving the page:
const AI_BOTS = /GPTBot|ChatGPT-User|OAI-SearchBot|ClaudeBot|Claude-User|PerplexityBot|CCBot/i;
const GATEWAY = "https://permit402.com/g/forecast-co";
app.use("/forecasts", (req, res, next) => {
if (!AI_BOTS.test(req.get("user-agent") ?? "")) return next(); // people: carry on
res.status(402)
.set("Link", `<${GATEWAY}/llms.txt>; rel="describedby"`)
.json({
message: "This data is available to AI agents through our paid API.",
api: GATEWAY,
how_to_pay: `${GATEWAY}/llms.txt`,
prices: `${GATEWAY}/.well-known/x402`,
});
});
Reading your dashboard
Choose a period at the top: the last 7, 30 or 90 days, or all time. The Your gateways page adds up every gateway. Each gateway's page shows these numbers:
| Earned | USDC actually paid to your wallet. Live earnings are real money. Test earnings, from the Base Sepolia test network, are counted separately and are worth nothing. |
| Paid calls | Calls that were paid for and succeeded. |
| Quotes | Times an agent was told the price (a 402). Agents often ask for a price first and pay on the next try, so this is usually higher than paid calls. |
| Rate | Paid calls as a share of quotes. A low rate can mean your price is too high, or that agents are only browsing. |
| Failed, not charged | Paid attempts where your API returned an error, so the payment was never taken. If this grows, check your API. |
| Payments rejected | Payments the gateway refused: invalid, for the wrong amount or network, already used once (a replay), or failed to settle. Nobody was charged. |
| Free calls / Blocked | Requests that matched no price, either passed to your API free or refused, depending on your setting. |
| Agents reading your listing | Reads of your gateway's llms.txt and price list. It's a sign of interest from agents and the tools that index them. |
By endpoint breaks all of this down per price, so you can see which endpoints or tools earn. Changing a price's amount keeps its history. Changing its method or path starts a new row.
Download payments (CSV) exports every payment in the chosen period, with time, amount, payer wallet and a link to the receipt on the blockchain, ready for a spreadsheet or your accountant.
MCP servers
Choose An MCP server when creating the gateway, and point it at your server's Streamable HTTP endpoint, e.g. https://mcp.yourcompany.com/mcp. Then price tools by name instead of paths:
| Tool | Price |
|---|---|
radar_image | $0.10 |
history_* | $0.05 (wildcards work) |
- In
tools/list, agents see the price added to each paid tool's description. - Calling a paid tool without paying returns the price. x402 MCP clients then pay inside the tool call (
_meta["x402/payment"]) and get a receipt back in the result. - The payment only settles if the tool succeeds. A tool error means no charge.
- Batched JSON-RPC requests aren't supported. Send one request at a time.
What agents see
- The agent calls a paid URL and gets
402 Payment Required, with the price in thePAYMENT-REQUIREDheader: amount, network, your wallet and the payment scheme. - It signs a USDC payment and retries with a
PAYMENT-SIGNATUREheader. - The gateway checks the payment, calls your API, and settles only if your API answers with a status below 400. The receipt comes back in
PAYMENT-RESPONSE.
Each payment buys exactly one call: replaying it is refused. Agents can discover your prices for free at <your gateway URL>/.well-known/x402.
Headers and limits
Your API receives
x-permit402-secret | Your gateway's secret. Reject requests without it. |
x-permit402-gateway | The gateway's name in the URL. |
x-permit402-payer | On paid calls, the wallet address that paid. |
| Everything else | The agent's other headers, method, path, query and body are passed through. Cookies and payment headers are removed. |
Agents receive
| Your response | Status, headers and body as your API sent them, except that Set-Cookie is removed and the response is sandboxed so it can't run scripts on our domain. |
x-permit402-charged | true or false on paid routes. |
PAYMENT-RESPONSE | The settlement receipt, when charged. |
Limits
| Request body | 10 MB |
| Your API's response time | 60 seconds |
| Gateways per account | 10 during the beta |
| Price changes | Reach every location within a few seconds |
Questions
- Do you hold my money?
- No. Payments go from the agent's wallet straight to yours. Permit402 never holds funds.
- What does it cost?
- Nothing during the beta, and we don't take a cut of payments.
- What if my API is down or returns an error?
- The agent isn't charged. A payment only settles when your API returns a status below 400.
- Can I pause charging?
- Yes. Pause in the dashboard stops the gateway within a few seconds. Resume brings it back.
- I lost my admin key.
- Keys can't be recovered because we only store a fingerprint of them, so keep yours in a password manager. If you think it has leaked, sign in and press Change admin key: the old key stops working straight away.
- Which agents can pay?
- Anything that speaks x402 version 2: the official x402 SDKs, x402 MCP clients and agent wallets built on them.