s402
A chain-agnostic wire format for HTTP 402 — types, header encoding, and a six-scheme registry for machines paying machines over HTTP.
Retired September 2026 in favor of x402, the open standard.
The problem
HTTP 402 has been reserved since 1999 with no wire format to fill it. Coinbase’s x402 proved the shape on EVM, but ships one payment model — exact, a single one-shot transfer. An agent that wants metered draw-down, a stream, or escrow with an arbiter has nowhere to put it.
The parts
Four layers, no runtime. Types describe six schemes: exact, upto, prepaid, stream, escrow, unlock. HTTP encodes them as base64 JSON in a header or raw JSON in a body, across HTTP, MCP and A2A carriers. Errors are 19 codes, each carrying a retryable flag and a recovery hint.
Why it holds
The chain-agnostic claim is enforced, not asserted. test/boundary.test.ts greps src/ for 11 forbidden patterns — Sui address regexes, Solana base58, ethers imports — and fails the build on a hit. It exists because a Sui address regex once reached http.ts and survived several sessions before a human caught it. 167 conformance vectors pin the wire format, and 46 of them are inputs that must be refused.
What it does not do
It does not move money. No custody, no facilitator, no fiat rail — settlement lives in @sweefi/sui. If you are EVM-only and exact covers you, x402 is the better choice, and by the whitepaper’s own modelled figures x402 on Solana is cheaper for one-shot calls: ~$0.25 per 1K against ~$7.00 for s402 Exact on Sui. The unlock scheme is still partial — it needs a key server that is not built.
v0.9.0 on npm, deprecated · 1,108 tests across 29 files · zero runtime dependencies
402 challenge — server side
import { encodePaymentRequired } from 's402' const header = encodePaymentRequired({ s402Version: '1', accepts: ['exact', 'prepaid'], network: 'sui:mainnet', asset: '0x2::sui::SUI', amount: '1000000', payTo: '0xabc…', }) // header.length === 176
Refusal — the branch that matters
catch (e) { if (e instanceof s402Error) { e.code // 'INVALID_PAYLOAD' e.retryable // false e.suggestedAction // 'Check payload format and // re-sign the transaction' } }
A refusal carries a code to branch on, a retryable flag, and the fix — so a client never parses an error string to decide what to do next.
What earns the word
- chain-agnostic
- a test greps src/ for 11 chain-specific patterns and fails the build
- conformance
- 167 vectors across 14 files; 46 must be refused
- wire-compatible
- x402’s header names, plus an opt-in compat layer
- zero deps
- package.json has no dependencies key at all
- shipped
- 7 of 12 ADRs read Implementation: shipped. Unlock is partial
- settlement
- not here — it lives in @sweefi/sui. This moves no money
A sequence, with an atomicity bracket