Skip to main content

Paywalls API

HTTP API reference for Gate. Create and manage x402 paywalls that charge agents per call, and read per-paywall earnings.

3 min read
View MarkdownEdit on GitHub

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

FieldTypeDescription
idstringPaywall ID, pw_<...>
project_idstringOwning project
slugstringURL slug; global namespace. /^[a-z0-9_-]+$/
namestringDisplay name, max 128
upstream_urlstringWhere paid calls are proxied
pricestringUSDC per call, decimal (min "0.01")
price_atomicstringSame price in atomic USDC (6 dp)
currencystringUSDC
pricing_modelstringflat (default), tiered, dynamic, or metered
payto_kind"provisioned" | "byo"Where settlement lands
payto_addressstring | nullThe 0x address for byo
consumer_idstring | nullThe consumer whose wallet receives for provisioned
activebooleanPaused paywalls return 402 with no settlement
gateway_urlstringhttps://gw.codespar.dev/<slug>
created_atstringISO 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 of consumer_id (required for this kind).
  • payto.kind: "byo" — settles to payto.address (a 0x EVM address).

Response201 Created with the paywall object.

Pricing models

pricing_model selects how the per-call price is resolved:

ValuePrice per callStatus
flatprice, the same on every call. The defaultLive
tieredprice steps down as the paywall's settled count crosses configured thresholdsLive
dynamicResolved per call from the paywall's dynamic ruleLive
meteredA ceiling is charged up front, then the actual cost is metered from the upstream response and the difference is refunded on-chain. See MeterBeta

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

HTTPcodewhen
400invalid_bodybody failed validation
403forbiddennon-admin caller on an admin endpoint
404paywall_not_foundunknown id in this project
409slug_conflictthe slug is already taken

See also

  • Monetized MCP servers: per-tool pricing for an MCP server you already run, managed on /v1/mcp-servers and served at gw.codespar.dev/mcp/<slug>.
  • Getting discovered: the public manifest at gw.codespar.dev/.well-known/x402 that lists your active paywalls with their x402 terms.
Paywalls API | CodeSpar