L402-aware fetch client for paid HTTP resources.
This package handles the full payment challenge flow:
- parse
402 Payment Requiredresponses - support both
L402andLSATschemes - pay using caller-provided hooks
- retry with
Authorizationproof - cache proofs locally to avoid duplicate payments
Want to run this immediately? See Examples (Fastest Way to Run).
- Node.js
>=22 - npm
npm install @zbdpay/agent-fetchimport { agentFetch, FileTokenCache } from "@zbdpay/agent-fetch";
const tokenCache = new FileTokenCache(`${process.env.HOME}/.zbd-wallet/token-cache.json`);
const response = await agentFetch("https://example.com/protected", {
tokenCache,
maxPaymentSats: 100,
pay: async (challenge) => {
// Pay challenge.invoice with your wallet implementation.
// Return preimage, plus optional paymentId/amountPaidSats.
return {
preimage: "<payment-preimage>",
paymentId: "<payment-id>",
amountPaidSats: challenge.amountSats,
};
},
waitForPayment: async (paymentId) => {
// Optional poller for async settlement.
// Return pending/completed/failed.
return {
status: "completed",
paymentId,
preimage: "<payment-preimage>",
amountPaidSats: 21,
};
},
});
const body = await response.json();
console.log(response.status, body);- If cached auth exists and is not expired, request is sent immediately with proof.
- If response is not
402, original response is returned untouched. - If response is
402, challenge is parsed fromWWW-Authenticateand/or JSON body. - If payment succeeds, proof is generated as
<SCHEME> <macaroon-or-token>:<preimage>and request is retried. - If
maxPaymentSatsis set and challenge exceeds it, call fails before payment. - If async settlement is used and times out, call fails with a timeout error.
Exports from src/index.ts:
agentFetchrequestChallengepayChallengefetchWithProofFileTokenCache- types:
AgentFetchOptions,PaymentChallenge,PaidChallenge,PaymentSettlement,TokenCache,TokenRecord,ChallengeScheme
pay(required): function to pay a parsed challengewaitForPayment(optional): poller for async settlementtokenCache(optional): token cache backendrequestInit(optional): forwarded fetch optionsfetchImpl(optional): custom fetch implementationmaxPaymentSats(optional): payment guardrailpaymentTimeoutMs(optional, default30000)paymentPollIntervalMs(optional, default300)now,sleep(optional testability hooks)
FileTokenCache stores per-URL tokens as JSON and writes atomically.
- no
expiresAt: token is reused until overwritten/deleted - with
expiresAt: expired token is evicted on read
Default cache location is chosen by the caller. In this suite, agent-wallet uses ~/.zbd-wallet/token-cache.json.
If you want a working paid-request flow in minutes, start with these scripts before wiring your own app code.
examples/zbd-agent-fetch.mjs: end-to-end paid fetch using ZBD API for invoice paymentexamples/fetch-with-known-proof.mjs: call a protected endpoint with a precomputed L402 token
Run from this repo:
npm run build
PROTECTED_URL="http://localhost:8787/protected" ZBD_API_KEY=<your_api_key> npm run example:zbdIf you already have an authorization token:
PROTECTED_URL="http://localhost:8787/protected" L402_AUTHORIZATION="L402 <macaroon>:<preimage>" npm run example:proofnpm run build
npm run test
npm run lint
npm run typecheck
npm run smoke:imports
npm run example:zbd
npm run example:proof
npm run release:dry-run@zbdpay/agent-walletuses this package forzbdw fetch@zbdpay/agent-payprovides middleware that emits L402 challenges this client can consume