Paywalls API
HTTP API reference for Gate. Create and manage x402 paywalls that charge agents per call, and read per-paywall earnings.
Paywalls API
REST API for Gate, the x402 gateway. A paywall charges agents per call for an endpoint you own, settling in USDC over x402.
Base URL: https://api.codespar.dev
All endpoints require authentication. See Authentication.
Endpoints marked admin require a bearer api key OR service auth with an
x-codespar-user header for an account admin/owner. Paywalls are
project-scoped.
Paywall object
| Field | Type | Description |
|---|---|---|
id | string | Paywall ID, pw_<...> |
project_id | string | Owning project |
slug | string | URL slug; global namespace. /^[a-z0-9_-]+$/ |
name | string | Display name, max 128 |
upstream_url | string | Where paid calls are proxied |
price | string | USDC per call, decimal (min "0.01") |
price_atomic | string | Same price in atomic USDC (6 dp) |
currency | string | USDC |
pricing_model | string | flat (default), tiered, dynamic, or metered |
payto_kind | "provisioned" | "byo" | Where settlement lands |
payto_address | string | null | The 0x address for byo |
consumer_id | string | null | The consumer whose wallet receives for provisioned |
active | boolean | Paused paywalls return 402 with no settlement |
gateway_url | string | https://gw.codespar.dev/<slug> |
created_at | string | ISO 8601 |
Create a paywall
POST /v1/paywalls (admin)
Body
{
"slug": "market-data",
"name": "Market data API",
"upstream_url": "https://api.yourservice.com/quote",
"price": "0.01",
"currency": "USDC",
"consumer_id": "your-consumer",
"payto": { "kind": "provisioned" }
}payto.kind: "provisioned"— settles to the CodeSpar wallet ofconsumer_id(required for this kind).payto.kind: "byo"— settles topayto.address(a0xEVM address).
Response — 201 Created with the paywall object.
Pricing models
pricing_model selects how the per-call price is resolved:
| Value | Price per call | Status |
|---|---|---|
flat | price, the same on every call. The default | Live |
tiered | price steps down as the paywall's settled count crosses configured thresholds | Live |
dynamic | Resolved per call from the paywall's dynamic rule | Live |
metered | A ceiling is charged up front, then the actual cost is metered from the upstream response and the difference is refunded on-chain. See Meter | Beta |
Gateway protocol
The 402 challenge is delivered base64-encoded in the PAYMENT-REQUIRED
response header (x402Version 2). The JSON body of the 402 is a
human-readable hint: clients read the header, not the body.
Payment goes back in the PAYMENT-SIGNATURE request header. X-PAYMENT is
still accepted for x402 v1 compatibility, and a v2 client may send both.
The settlement result comes back in the PAYMENT-RESPONSE response header
(also mirrored to X-PAYMENT-RESPONSE for v1 clients). Both are listed in
Access-Control-Expose-Headers, so a browser client can read them.
An optional Idempotency-Key request header makes a paywall call settle at
most once, even across re-signed retries. A retry that arrives while the first
is still in flight gets 409 idempotency_in_progress; a completed call
replays the stored response with Idempotent-Replay: true.
List paywalls
GET /v1/paywalls
Response
{ "paywalls": [/* Paywall */] }Get a paywall
GET /v1/paywalls/:id — the paywall object, or 404 paywall_not_found.
Update a paywall
PATCH /v1/paywalls/:id (admin)
Body (at least one field)
{ "name": "...", "upstream_url": "...", "price": "0.02", "active": false }Set active: false to pause (callers get 402, nothing settles); true to resume.
Delete a paywall
DELETE /v1/paywalls/:id (admin) — 204 No Content. Paywalls are config, not
audit data; settled receipts are retained independently.
Earnings
GET /v1/paywalls/:id/stats
Read-only aggregate over the paywall's settled payments.
Response
{
"paywall_id": "pw_...",
"slug": "market-data",
"currency": "USDC",
"settled_count": 128,
"gross_atomic": "1280000",
"gross": "1.28",
"refunded_atomic": "0",
"refunded": "0.00",
"net_atomic": "1280000",
"net": "1.28",
"last_settled_at": "2026-07-10T21:04:11Z"
}gross is what payers authorized, refunded is what went back to them, and
net is what the paywall kept. On flat, tiered, and dynamic paywalls there are
no refunds, so gross equals net. On a metered
paywall gross is the sum of the signed ceilings and net is the actual
metered revenue, so the two read as ceiling versus actual.
Errors
| HTTP | code | when |
|---|---|---|
400 | invalid_body | body failed validation |
403 | forbidden | non-admin caller on an admin endpoint |
404 | paywall_not_found | unknown id in this project |
409 | slug_conflict | the slug is already taken |
See also
- Monetized MCP servers: per-tool
pricing for an MCP server you already run, managed on
/v1/mcp-serversand served atgw.codespar.dev/mcp/<slug>. - Getting discovered: the public
manifest at
gw.codespar.dev/.well-known/x402that lists your active paywalls with their x402 terms.
Meter
Post-paid metered pricing on a Gate paywall. The buyer signs a spending ceiling once, your upstream reports the actual work in a response header, and the difference is refunded on-chain automatically.
Payment Links API
HTTP API reference for Collect payment links. Shareable pay URLs where an agent pays over x402/USDC or a person pays via Pix, verified before fulfillment.