Simple 402 Payments
The full micropayment framework is more than a small service needs to charge for a single endpoint. This is the short version: a price and an identity key in the refusal, a payment in the retry.
Summary
- Why
- Servers need a simple way to require and verify a small payment for a single HTTP request without building session state, negotiation logic, or an account system.
- What
- BRC-121 is a lightweight protocol that uses the HTTP 402 status code and a handful of custom headers to let a server request a Bitcoin SV micropayment and a client pay it in one extra round trip.
- How
- A server responds 402 with a price and its public key, the client builds a BRC-29 payment transaction addressed to a key derived from that response, encodes it as BRC-95 BEEF, and resends the request with payment headers that the server verifies and internalizes via its wallet before serving the resource.
What this lets you do
- Charge a satoshi price for any HTTP resource
- Verify payment per-request with no server-side session state
- Reject stale or replayed payment headers automatically
- Serve protected content only after wallet internalization succeeds
- Skip the 402 round trip on the client by caching paid URLs
Written by claude-sonnet-5 from the specification text. Where the two differ, the original is correct.
Reference for an AI
Everything an assistant needs to answer questions about BRC-121 accurately, including what it depends on.
The specification
Abstract
This BRC specifies a lightweight protocol for monetizing HTTP resources using the 402 Payment Required status code and a set of custom HTTP headers. A server advertises a price and its identity key in the 402 response. A client constructs a BRC-29 payment transaction, encodes it in BRC-95 BEEF format, and retransmits the original request with payment headers. The server validates the transaction, internalizes the payment via its wallet, and serves the protected resource. The protocol is stateless from the server's perspective: each request is independently authorized by the presence or absence of valid payment headers.
Status Note
This document specifies a deliberately small BSV-specific 402 payment profile.
It should be read as an alternative to, not a replacement for, BRC-105. Implementers that need the broader BRC-103/BRC-104-authenticated monetization framework should use BRC-105, optionally with BRC-118 for multipart transport. Implementers that want conformance to the frozen external x402 specification should use BRC-120.
Motivation
HTTP 402 remains reserved for future use in current HTTP semantics (RFC 9110), but no general web-standard payment mechanism was adopted. Meanwhile, BSV transaction fees are low enough to make per-request payments practical for content such as articles, API calls, and media.
Existing BSV payment protocols (BRC-27 Direct Payment Protocol, BRC-105 HTTP Service Monetization Framework, BRC-120 x402) each define comprehensive negotiation mechanisms. This BRC targets a simpler use case: a client that already holds a BRC-42-compatible wallet pays for an HTTP resource in a single round-trip pair (one 402 response, one paid request) using only standard HTTP headers and BRC-29 payment remittance.
Design goals:
- Minimal surface area -- no JSON body negotiation, no challenge tokens, no session state.
- BRC-29 compatible -- payment derivation uses the same protocol ID (
2-3241645161d8), derivation prefix, and derivation suffix scheme defined in BRC-29. - BEEF transport -- the payment transaction is transmitted as a base64-encoded BRC-95 BEEF blob in a single header, carrying full SPV ancestry.
- Stateless verification -- the server validates and internalizes the payment on each request independently. Caching of paid status is a client-side optimization, not a protocol requirement.
Specification
1. Header Namespace
All protocol headers use the prefix x-bsv-. The following headers are defined:
| Header | Direction | Description |
|---|---|---|
x-bsv-sats | Server to client | Required satoshi amount for the resource. |
x-bsv-server | Server to client | Server's compressed, hex-encoded secp256k1 identity public key. |
x-bsv-beef | Client to server | Base64-encoded BRC-95 BEEF transaction containing the payment. |
x-bsv-sender | Client to server | Client's compressed, hex-encoded secp256k1 identity public key. |
x-bsv-nonce | Client to server | Base64-encoded BRC-29 derivation prefix for the payment. |
x-bsv-time | Client to server | Derivation suffix: a Unix millisecond timestamp as a decimal string (see Section 3). |
x-bsv-vout | Client to server | Output index (zero-based, decimal string) of the payment output within the transaction. |
2. Server: 402 Response
When a client requests a protected resource without payment headers, the server MUST respond with:
- HTTP status
402 Payment Required - Header
x-bsv-satsset to the required price in satoshis - Header
x-bsv-serverset to the server's identity public key - An empty body (the server MUST NOT serve the protected content)
The server SHOULD also set appropriate CORS headers to expose x-bsv-sats and x-bsv-server to browser-based clients:
Access-Control-Expose-Headers: x-bsv-sats, x-bsv-server
3. Client: Payment Construction
Upon receiving a 402 response, the client:
- Reads
x-bsv-satsto determine the required payment amount. - Reads
x-bsv-serverto obtain the server's identity key (the payment recipient). - Generates a derivation prefix as specified in BRC-29.
- Sets the time value to the current Unix timestamp in milliseconds as a decimal string (e.g.
1719500000000). This serves as both a freshness indicator for the server and, after base64 encoding, the derivation suffix required by BRC-29. - Base64-encodes the time value for use as the derivation suffix in key derivation. Uses BRC-42 key derivation with the invoice number format
2-3241645161d8-<nonce> <base64(time)>to derive the recipient's public key. - Constructs a BSV transaction with a P2PKH output of the required satoshi amount locked to the derived public key.
- Serializes the transaction in BRC-95 BEEF format and encodes it as base64.
4. Client: Paid Request
The client retransmits the original HTTP request with the following headers added:
| Header | Value |
|---|---|
x-bsv-beef | Base64-encoded BEEF transaction |
x-bsv-sender | Client's identity public key (hex) |
x-bsv-nonce | Base64-encoded derivation prefix |
x-bsv-time | Unix millisecond timestamp (decimal string) |
x-bsv-vout | Output index of the payment output (decimal string) |
The request method, URL, and any other headers or body MUST be identical to the original request.
5. Server: Payment Validation
Upon receiving a request with payment headers, the server MUST:
- Verify that all five client headers (
x-bsv-beef,x-bsv-sender,x-bsv-nonce,x-bsv-time,x-bsv-vout) are present. If any are missing, respond with 402. - Parse
x-bsv-timeas a Unix millisecond timestamp. If the value is not a valid number, or differs from the server's current time by more than 30 seconds, the server MUST reject the request and respond with 402. This prevents capture and delayed replay of payment headers. - Decode the BEEF transaction from base64.
- Internalize the payment using the wallet's
internalizeActionoperation with:- The decoded transaction bytes
- Protocol:
wallet payment - Payment remittance containing
derivationPrefix(fromx-bsv-nonce),derivationSuffix(base64-encoded from the rawx-bsv-timevalue), andsenderIdentityKey(fromx-bsv-sender) - Output index from
x-bsv-vout
- Check the
isMergefield of the internalization result. IfisMergeistrue, the transaction was already known to the wallet, indicating a replayed payment. The server MUST reject the request and respond with 402. - If internalization succeeds and
isMergeisfalse, serve the protected resource with HTTP status200. - If internalization fails (invalid transaction, insufficient amount, invalid derivation, or SPV failure), respond with 402.
The server MUST NOT maintain per-client payment state. Each request is independently authorized. Replay protection is provided by two mechanisms: the timestamp freshness check on x-bsv-time (preventing delayed replay), and the wallet's isMerge flag (preventing immediate replay of the same transaction).
6. Client: Caching (Optional)
A client MAY cache the set of URLs for which payment has been accepted and skip the 402 round-trip on subsequent requests to the same URL. This is a client-side optimization. The server makes no guarantees about how long a payment remains valid.
7. Error Handling
- If the server cannot initialize its wallet, it SHOULD return
500 Internal Server Error. - If payment validation throws an exception, the server SHOULD return
402to allow the client to retry. - A client that receives a
402after submitting payment headers SHOULD NOT automatically retry without user confirmation to avoid double-spending.
Implementations
-
@bsv/402-pay(npm package) -- A production npm package providing both server middleware and client fetch wrapper. Install withnpm install @bsv/402-pay. Source: github.com/bsv-blockchain/402-pay.// Server (Express) import { createPaymentMiddleware } from '@bsv/402-pay/server' app.get('/content/:id', createPaymentMiddleware({ wallet, calculatePrice: () => 100 }), handler) // Client import { create402Fetch } from '@bsv/402-pay/client' const fetch402 = create402Fetch({ wallet }) const res = await fetch402('https://example.com/content/foo') -
Server (Express/Node.js) -- A reference server using
@bsv/402-payis available at github.com/bsv-blockchain-demos/402-articles. -
Client (React Native WebView) -- A reference client implementation intercepts 402 responses in a fetch wrapper, constructs payment headers using a BRC-42-compatible wallet, and retransmits the request. Source: github.com/bsv-blockchain/bsv-browser.
-
Client (Browser Extension) -- A Chromium extension that intercepts 402 responses via the declarativeNetRequest API and retransmits with payment headers. Source: github.com/bsv-blockchain/402-extension.
-
Wallet Compatibility -- Any wallet implementing the
WalletInterfacefrom@bsv/sdkwith support forinternalizeActionandgetPublicKey({ identityKey: true })can be used on either side of the protocol.
Implementations
Source Code
References
- BRC-29: Simple Authenticated BSV P2PKH Payment Protocol
- BRC-42: Key Derivation Scheme
- BRC-43: Security Levels and Protocol IDs
- BRC-95: BEEF Transaction Format
- BRC-105: HTTP Service Monetization Framework
- BRC-120: x402 Stateless Settlement-Gated HTTP Protocol
- RFC 9110 Section 15.5.3: 402 Payment Required
- Reference server implementation: https://github.com/bsv-blockchain-demos/402-articles
- Reference client implementation: https://github.com/bsv-blockchain/bsv-browser