The middleware
For a Next.js route handler, wrap the handler. For Express, the same file exports otsukaiExpress(options), a middleware you put in front of a route.
// app/api/report/route.ts
import { otsukaiPaywall } from "./otsukai-paywall.mjs";
export const GET = otsukaiPaywall(
{
price: 50_000n, // $0.05 in USDG, 6 decimals
payTo: "0xYourAddress",
description: "Daily report",
facilitator: "https://otsukai.dev/api/facilitator", // or settleKey: process.env.SETTLE_KEY
},
async (req) => Response.json({ report: "…" }),
);otsukaiPaywall answers 402 when there is no X-PAYMENT header. When there is one, it checks that the payee, the amount and the resource hash match the request, settles, and only then calls your handler. Download it as one file, otsukai-paywall.mjs (viem only). With facilitator it settles through the Otsukai facilitator; with settleKey it calls the contract itself.
Settling
You have two ways to settle:
- Call the contract yourself. Submit
account.pay(payment, sig)from any address you control. You pay the gas, about 0.000003 ETH per payment at current prices. - Use the Otsukai facilitator.
POST /api/facilitator/verifyand/settlewith{ paymentHeader }. It settles only for payees listed in the directory, from $0.005 per payment, with a rate limit per account.
$ curl -X POST https://otsukai.dev/api/facilitator/verify -d '{"paymentHeader":"eyJzY2hlbWUiOi…"}'< { "isValid": true, "payer": "0x…" }$ curl -X POST https://otsukai.dev/api/facilitator/settle -d '{"paymentHeader":"eyJzY2hlbWUiOi…"}'< { "success": true, "txHash": "0x…", "receiptId": "0x…" }
Getting listed
The directory shows only endpoints that really accept Otsukai. At launch it holds Otsukai's two endpoints. To be listed, run the middleware on a public URL and write to us on X.