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

Hono

Charge per call for Hono routes on Node, Bun, Deno, and Cloudflare Workers.

Public Beta · early access
server.ts
import { Hono } from "hono";
import { tollstile } from "@tollstile/hono";
import { toll } from "./toll";

const app = new Hono();

app.post(
  "/v1/generate",
  tollstile(toll.price("$0.04"), {
    principal: (c) => (c.get("user") ? { id: c.get("user").id } : null),
  }),
  async (c) => {
    const payment = c.get("payment"); // typed from your rails
    return c.json(await generate(await c.req.json()));
  },
);

export default app;
OptionPurpose
principalResolves the authenticated caller for subscriber() and credits()
  • The resource name is METHOD /route/:pattern from Hono's matched route.
  • The handler succeeds when it returns a response below 400 without throwing; otherwise the charge is released or refunded.
  • Receipt headers are appended to your response.
  • c.get("payment") exposes the payment, including fulfill({ amount }) for upTo() prices.
  • If settlement is rejected after the handler, the response is replaced by a fresh 402 with error code settlement_rejected; the payer does not get the output. If the outcome is unknown, the response is sent without a receipt and reconciliation resolves the charge.

Tutorial: Monetize an API with x402.

Idempotent retries

The adapter forwards Idempotency-Key to core automatically. Send the same key on the first paid request and its retries; keep the request unchanged. A completed payment returns 409 already_paid with chargeId, settlement, and result; the handler does not run again. In-flight requests return 409 request_in_progress, unknown outcomes return 503 payment_outcome_unknown, and a key used for a different request returns 422 idempotency_key_reused.

Use payment.fulfill({ resultRef }) to record where your application stored its result. Tollstile does not cache the response. Released or refunded attempts may run again; subscriber grants are not deduplicated. See Idempotency for matching, rail identity scopes, and retry limits.

On this page