BRC-121 monetizes HTTP API endpoints with a single-round-trip payment flow: client requests a resource, server responds with 402 and payment instructions (a nonce), client derives a payment address, constructs a BSV transaction, and re-sends the original request with payment headers. Server validates and serves the resource (or rejects with another 402).
| Field | Value |
|---|---|
| Format | OpenAPI 3.1 |
| Version | 1.0.0 |
| Status | stable |
| Implementation | @bsv/402-pay |
Monetize APIs without subscriptions or token systems. Traditional APIs require accounts, credit cards, or API keys. BRC-121 enables instant monetization: each request is micro-paid in satoshis. No signup, no account needed—just pay per request and immediate access.
Request-bound payment derivation. The client supplies a fresh nonce and
timestamp that participate in payment-key derivation. Those values bind the
payment proof, but they are not a replay database by themselves. The server
must also validate freshness and request fields, validate the remittance in its
wallet, and atomically claim the transaction ID in an independent replay store.
An exact wallet isMerge detail is useful defense in depth, but it is not part
of the public BRC-100 result contract and cannot be the only replay control.
No authentication session required. Requests do not require BRC-103/104 mutual-auth state. Wallet transaction state and any application cache/replay policy are still stateful security dependencies. The built-in replay store is bounded and process-local; multi-process services require shared durable atomic claim state.
Single round-trip with payment:
Client → Server GET /protected-resource (no payment headers)
Server → Client HTTP 402 response
x-bsv-server: <serverPublicKey> — Server's identity keyx-bsv-sats: <amount> — Satoshis requiredClient (constructs payment)
wallet.createAction() with one P2PKH output<nonce> <base64(timestamp)>(pubKeyA, pubKeyB) from sender+server keysClient → Server same request with payment headers
x-bsv-beef: <beefBase64> — Atomic BEEF transactionx-bsv-sender: <clientPublicKey> — Sender's identity keyx-bsv-nonce: <nonce> — Client-generated derivation prefixx-bsv-time: <timestamp> — Unix millisecond timestampx-bsv-vout: <outputIndex> — Which output contains the paymentServer (validates & serves)
wallet.internalizeAction() with BEEFaccepted === true result and rejects an exact isMerge === true detail| Method | Path | Purpose | Request | Response |
|---|---|---|---|---|
| GET/POST/etc. | /{resourcePath} | First request (unpaid) | (none) | 402 + x-bsv-sats, x-bsv-server |
| GET/POST/etc. | /{resourcePath} | Second request with payment | x-bsv-beef, x-bsv-sender, x-bsv-nonce, x-bsv-time, x-bsv-vout | 200 + resource body, or 402 on failure |
import express from 'express'
import { createPaymentMiddleware } from '@bsv/402-pay/server'
import { ServerWallet } from '@bsv/simple/server'
const wallet = await ServerWallet.create({
privateKey: process.env.SERVER_PRIVATE_KEY!,
network: 'main',
storageUrl: 'https://store-us-1.bsvb.tech'
})
const app = express()
app.use(
createPaymentMiddleware({
wallet,
replayStore: sharedAtomicReplayStore,
calculatePrice: path => {
// Pricing logic
if (path === '/premium') return 500 // 500 sats
if (path === '/free') return 0 // Free
return 100 // Default 100 sats
}
})
)
// Protected endpoint
app.get('/premium', (req, res) => {
// If we reach here, payment was accepted
res.json({
content: 'Premium article here',
paidBy: req.payment.senderIdentityKey,
amount: req.payment.satoshisPaid,
txid: req.payment.txid
})
})
app.listen(3000)Client-side (using create402Fetch):
import { create402Fetch } from '@bsv/402-pay/client'
import { WalletClient } from '@bsv/sdk'
const wallet = new WalletClient()
// Fetch wrapper that auto-handles 402
const fetch402 = create402Fetch({ wallet })
const response = await fetch402('https://api.example.com/premium')
const data = await response.json()
console.log(data.content) // Automatic payment + resource retrievalThere is no standalone BRC-121 vector directory in the current conformance corpus. Related portable fixtures live in conformance/vectors/wallet/brc29/payment-derivation.json for payment derivation and conformance/vectors/wallet/brc100/ for wallet methods used by payment clients and servers.
| Package | Notes |
|---|---|
| @bsv/402-pay/server | Server-side middleware for independent 402 handling without BRC-31 auth |
| @bsv/402-pay/client | Client-side fetch wrapper that auto-detects 402 and constructs payment headers |
| @bsv/payment-express-middleware | Related legacy authenticated JSON flow; not a BRC-121 implementation |
internalizeAction() for payment validation