Rails
How an agent pays, and what each payment protocol can do.
A rail is how a payment is made and proven. It turns a price into an offer in its own asset, issues the protocol's challenge, verifies proofs, and settles, refunds, releases, and looks up charges through its provider.
Rails are not policies
A subscription is not a payment protocol, and x402 is not a pricing model. Rails decide how a payment is made; access policies decide whether a caller pays. See Access policies.
Capabilities
Rails genuinely differ. Each declares what it can do instead of pretending to be identical.
| Capability | Meaning |
|---|---|
flows | Which flows it supports |
authorization | single or reusable proofs |
variableAmount | Can settle less than the authorized maximum |
quotes | Carries a signed quote through its protocol |
refund · partialRefund | Can return settled money |
lookup | Can ask its provider what happened to a charge — required |
A rail without lookup is refused: without it, an unknown outcome could only be guessed.
Execution plan
Each route is compiled against your rails when you define it. Rails that cannot serve the route are left out, with the reason; a route no rail can serve is refused at startup.
const toll = createTollstile({
rails: [x402({ ...x402Options, upto: { facilitatorAddress } }), mppStripe(stripeOptions)],
ledger,
secret,
});
const gate = toll.price(upTo("$1.00"));
console.log(toll.explain(gate));Route upTo("$1.00")
pricing: up_to, quote bound to: route
access: everyone pays
requirements: none
rails:
x402: authorization flow, settles after handler, single authorization, up-to amounts, release on handler failure
excluded:
mpp-stripe: needs the authorization flow. The rail supports: upfront.gate.plan holds the same information as data, for tests and tooling.
When a provider is down
- While issuing a challenge (a Lightning node cannot create an invoice), that rail is left out of the
402. If no rail can offer, the answer is503 payment_unavailable. - While verifying, the request gets
503and the handler does not run. - While settling or refunding, the charge becomes
unknownand reconciliation asks the provider later.
Payer evidence
Some proofs must be kept to settle after a crash, such as a signed x402 payload. A rail keeps them in the authorization's data only until the charge is final, then redact drops them. Evidence never appears in logs, errors, events, or receipts.
Price and asset
A route is priced in a currency; a rail settles in an asset. The rail's offer states the asset, network, integer amount, and the basis of conversion — par for a USD stablecoin configured as USD, or rate for a merchant-supplied rate. Tollstile never converts currencies on its own.
Available rails
| Rail | Authorization | Flows | Verification status |
|---|---|---|---|
| Test rail | single or reusable | authorization, upfront | Implemented; local only |
| x402 exact · upto | single | authorization | Tested against a fake facilitator, a simulated chain, and @x402/core; not verified live |
| MPP Stripe charge | single | upfront | Tested against an in-memory Stripe; not verified live |
| MPP Tempo charge | single | authorization (push: paid at verification) | Tested against a fake node; not verified live |
| MPP Tempo session | reusable | upfront | Experimental |
| L402 | reusable | authorization | Tested against macaroon vectors and a fake LND; not verified live |
| KYAPay | reusable | authorization | Tested against a fake Skyfire; not verified live |