Skip to main content
@zbdpay/agent-pay is the server half of the L402 protocol. It creates invoice-backed payment challenges and verifies payment proof before allowing access.

Install

npm install @zbdpay/agent-pay

Environment

VariableRequiredDefault
ZBD_API_KEYYes (unless passed in config)none
ZBD_API_BASE_URLNohttps://api.zbdpay.com

Quickstarts

import express from "express";
import { createExpressPaymentMiddleware } from "@zbdpay/agent-pay";

const app = express();

app.get(
  "/protected",
  createExpressPaymentMiddleware({
    amount: 21,
    apiKey: process.env.ZBD_API_KEY,
  }),
  (_req, res) => {
    res.json({ ok: true });
  },
);

PaymentConfig

type PaymentConfig<RequestLike = unknown> = {
  amount: number | ((request: RequestLike) => number | Promise<number>);
  currency?: "SAT" | "USD";
  apiKey?: string;
  tokenStorePath?: string;
};
SAT pricing is the default and the most direct production path.

402 Response Shape

WWW-Authenticate: L402 macaroon="<token>", invoice="<bolt11>"
{
  "error": {
    "code": "payment_required",
    "message": "Payment required"
  },
  "macaroon": "<token>",
  "invoice": "<bolt11>",
  "paymentHash": "<hash>",
  "amountSats": 21,
  "expiresAt": 1735766400
}

Error Codes

HTTPCodeMeaning
402payment_requiredNo valid payment proof provided
401invalid_credentialMacaroon signature invalid
401invalid_payment_proofPreimage mismatch
403resource_mismatchProof for different route
403amount_mismatchProof for different price
403token_expiredProof expired
500configuration_errorMissing API key/config
500pricing_errorDynamic pricing function failed
502invoice_creation_failedUpstream invoice creation failed

Runnable Example

The repository includes examples/http-server.mjs for a minimal paid route. From the agents workspace, run:
npm --prefix agent-pay run build
ZBD_API_KEY=<your_api_key> npm --prefix agent-pay run example:http-server
Optional verbose logs:
ZBD_PAY_DEBUG=1 ZBD_API_KEY=<your_api_key> npm --prefix agent-pay run example:http-server
Then hit the route with your wallet CLI:
zbdw fetch "https://api.example.com/protected" --max-sats 100

Storage

By default, settled token metadata is persisted at:
~/.zbd-wallet/server-tokens.json
Override via tokenStorePath when needed for your deployment model.