Hono
Charge per call for Hono routes on Node, Bun, Deno, and Cloudflare Workers.
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;| Option | Purpose |
|---|---|
principal | Resolves the authenticated caller for subscriber() and credits() |
- The resource name is
METHOD /route/:patternfrom Hono's matched route. - The handler succeeds when it returns a response below
400without throwing; otherwise the charge is released or refunded. - Receipt headers are appended to your response.
c.get("payment")exposes the payment, includingfulfill({ amount })forupTo()prices.- If settlement is rejected after the handler, the response is replaced by a fresh
402with error codesettlement_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.