npm packages are not installable yet: [email protected] can be published after 2026-09-16 10:30 UTC. Details
Tollstile

Test rail

Run the full payment lifecycle locally with no wallet, network, or account.

Public Beta · early access
import { createTollstile, memoryLedger, testRail } from "tollstile";

export const toll = createTollstile({ rails: [testRail()], ledger: memoryLedger() });

Send Payment: test to pay a fixed price, or pay against the quote from the 402:

Payment: test quote=<token> proof=p1 payer=agent_1 amount=$0.01 limit=$1.00 paymentId=operation_1
ParameterEffect
quoteThe quote token from the 402. Required on dynamic routes.
proofStable proof id. Without it, every request is a fresh payment.
payerPayer id, used by requirements and idempotency scope. Defaults to test-payer.
paymentIdProtocol idempotency key when no client key is supplied. The HTTP or MCP client key takes precedence.
amountMust equal the price, otherwise the proof is invalid.
limitCapacity of a reusable authorization.
signatureStands in for payer evidence a real rail keeps until the charge is final. It is stored in the authorization's data and dropped by the rail's redact once the charge is settled.
OptionDefaultEffect
authorization"single""reusable" behaves like an L402 credential or a KYAPay token
refundtruefalse behaves like a rail that cannot refund, such as x402: only the authorization flow is offered

Over MCP, send the same string in _meta["tollstile/test-payment"]; the receipt is _meta["tollstile/test-receipt"].

See Test payment failures for rail.simulate() and rail.effects.

Never alongside live rails

createTollstile() refuses a test rail configured together with a live rail.

Exercise retries

curl -i -H 'Payment: test proof=p1 payer=agent_1 paymentId=operation_1' localhost:3000/weather
curl -i -H 'Payment: test proof=p1 payer=agent_1 paymentId=operation_1' localhost:3000/weather

On the fixed-price quickstart route the first call succeeds and the second returns 409 already_paid. Omit paymentId and both client-key carriers to test 409 proof_already_used. With authorization: "reusable", omitting a key creates another charge instead. Payment: test alone uses a fresh proof ID on each request, so it does not test proof replay.

See Idempotency and the conformance kit.

On this page