s402v0.9.0 · TypeScript

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

AGENTRESOURCEGET /thing402 — this costs 0.05, here is where to send itHTTP 402 “Payment Required” — reserved in 1997 and thenleft unused, because no payer could act on it unattended.pay and fetch, in one programmable blockA Sui programmable transaction block: several operationssubmitted as one unit, committed whole or rejected whole.the thing, and the settlementCOMMITS OR FAILS TOGETHERno state where one happened and the other did notAtomicity is a property the chain grants or withholds. Aprotocol cannot add it — which is why s402 goes deepest on Sui.
The bracket is the entire argument for building deepest on Sui. Atomicity is a property the chain hands you or withholds, and a protocol cannot manufacture it. Draw the same sequence without the bracket and you have described a payment — not this one. Hover any dotted term for its definition.