Tollstile

Quotes

What the server offered — signed, immutable, and never stored.

Pre-release · planned API

Every 402 carries a quote: the resource, the price, whether it is a maximum, an offer per rail, a nonce, and an expiry. It is serialized as a compact token and signed with your secret.

{
  "error": "payment_required",
  "price": "$0.04",
  "quote": "eyJ2IjoxLC….x9Q…",
  "expiresAt": "2026-09-15T12:05:00.000Z",
  "accepts": [{ "rail": "x402", "asset": { "code": "USDC", "network": "eip155:8453", "scale": 6 }, "amount": "40000", "flow": "authorization" }]
}

Why quotes exist

  • The price charged is the price the payer saw. Dynamic prices can change between the 402 and the retry; the proof carries the quote back and Tollstile charges the quoted price.
  • Nothing is written for unpaid requests. Quotes are verified by signature, so the ledger only grows when someone actually pays.
  • A quote pays only for the request it priced. It commits to the method, path, query, and body (or MCP tool arguments), so a cheap quote cannot be spent on a larger request.
  • Evidence can bind to a request. The quote's nonce lets protocols such as AP2 bind a user mandate to this exact offer.

How rails carry a quote

Each rail puts the quote token inside its own protocol, where the payer's client echoes it: x402 in the requirement's extra, MPP in the challenge's opaque, L402 in a macaroon caveat, the test rail as quote=. Rails that cannot carry one declare quotes: false and serve fixed-price routes only.

Validation

A quote is honored only if its signature matches a configured secret, it has not expired, it was issued for the same resource, and the retried request matches its commitment. Rotate secrets by listing the new one first: secret: [next, previous].

Request commitment

commitThe quote is bound toDefault for
"request"method, resource, path and query, the exact body bytes; on MCP, the tool and its argumentsdynamic prices
"route"method and resourcefixed prices
(context) => stringmethod, resource, and the value you returnbodies that clients re-serialize

A mismatch returns 402 with reason: "quote_mismatch" and a fresh quote, before anything is written. Reusable authorizations are the exception: they pay each request's current price against their limit, so they are not held to one request. See Dynamic pricing.

On this page