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

Quickstart

Add pay-per-call pricing to an API in five minutes with the test rail. No wallet or account.

Public Beta · early access

This guide prices a Hono route with the test rail and memory ledger, so there is nothing to sign up for.

1. Install

npm install tollstile @tollstile/hono hono

Or start from a template: npx create-tollstile my-paid-api.

2. Price a route

server.ts
import { Hono } from "hono";
import { createTollstile, memoryLedger, testRail } from "tollstile";
import { tollstile } from "@tollstile/hono";

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

const app = new Hono();

app.get("/weather", tollstile(toll.price("$0.01")), (c) => {
  return c.json({ forecast: "clear" });
});

export default app;

3. Call it without paying

curl -i localhost:3000/weather
HTTP/1.1 402 Payment Required

{
  "error": { "code": "payment_required", "retryable": true, "action": "pay", "message": "Payment required.", "detail": null },
  "price": "$0.01",
  "quote": "eyJ2IjoxLCJpZCI6…",
  "accepts": [{ "rail": "test", "amount": "10000", "flow": "authorization", … }]
}

The quote is a signed record of what the server offered. It is never stored.

4. Pay with the quote

curl -i -H "Payment: test quote=eyJ2IjoxLCJpZCI6…" localhost:3000/weather
HTTP/1.1 200 OK
payment-receipt: test_settlement_chg_…

{ "forecast": "clear" }

The ledger now holds one authorization and one charge in settled/completed. For fixed prices, Payment: test without a quote also works.

5. Switch to a real rail

Replace testRail() with live rails and memoryLedger() with a database ledger. The route does not change. The tutorials walk through it:

Next

Retry the same operation

curl -i -H 'Payment: test' -H 'Idempotency-Key: weather-1' localhost:3000/weather
curl -i -H 'Payment: test' -H 'Idempotency-Key: weather-1' localhost:3000/weather

The first succeeds; the second returns 409 already_paid. The default test payer is stable even though Payment: test creates a fresh proof. See Idempotency.

On this page