s402
v0.2.3
Published
s402 — Chain-agnostic HTTP 402 wire format. Types, HTTP encoding, and scheme registry for five payment schemes. Wire-compatible with x402. Zero runtime dependencies.
Maintainers
Readme
s402
Sui-native HTTP 402 protocol. Atomic settlement via Sui's Programmable Transaction Blocks (PTBs). Includes an optional compat layer (s402/compat) for normalizing x402 input.
s402 is the Sui-native implementation of HTTP 402 (Payment Required) — an open protocol that lets AI agents pay for API calls in a single HTTP request with no per-call on-chain transaction. Unlike Coinbase's x402 on Base (EVM L2), s402 uses Sui's Programmable Transaction Blocks to reduce 1,000 payments to just 2 on-chain transactions via the Prepaid scheme, cutting per-call effective gas from $0.007 to $0.000014 and making micropayments economically viable for AI agents for the first time.
npm install s402
pnpm add s402
bun add s402
deno add npm:s402ESM-only. This package ships ES modules only (
"type": "module"). Requires Node.js >= 18. CommonJSrequire()is not supported.
Why s402?
HTTP 402 ("Payment Required") has been reserved since 1999 — waiting for a payment protocol that actually works. Coinbase's x402 proved the concept on EVM. s402 takes it further by leveraging what makes Sui different.
s402 vs x402
| | x402 (Coinbase) | s402 |
|---|---|---|
| Settlement | Two-step: verify then settle (temporal gap) | Atomic: verify + settle in one PTB |
| Finality | 12+ second blocks (EVM L1) | ~400ms (Sui) |
| Payment models | Exact (one-shot) only | Five schemes: Exact, Prepaid, Escrow, Unlock, Stream |
| Micro-payments | ~$1.60 gas per 1K calls on Base (broken) | $0.014 gas per 1K calls (prepaid) |
| Coin handling | approve + transferFrom | Native coinWithBalance + splitCoins |
| Agent auth | None | AP2 mandate delegation |
| Direct mode | No | Yes (no facilitator needed) |
| Receipts | Off-chain | On-chain NFT proofs |
| Compatibility | n/a | Optional x402 compat layer (s402/compat) |
s402 is Sui-native by design. These advantages come from Sui's object model, PTBs, and sub-second finality. They can't be replicated on EVM — and they don't need to be. x402 already handles EVM well. s402 handles Sui better.
Which Scheme Should I Use?
| Your situation | Scheme | Gas per 1K calls | Latency | |---|---|---|---| | One-time API call, simplest path | Exact | $7.00 | ~400ms | | High-frequency API (10+ calls) | Prepaid | $0.014 | ~0ms per call | | Buyer needs dispute protection | Escrow | $7.00 | ~400ms | | Selling encrypted content | Unlock | $7.00 | ~400ms | | Real-time billing (per-second) | Stream | variable | ~400ms setup |
Quick decision: Use Prepaid for AI agents making repeated API calls. Use Exact for everything else (it's the x402-compatible default). See the full guide for details.
Architecture
s402 <-- You are here. Protocol spec. Zero runtime deps.
|
|-- Types Payment requirements, payloads, responses
|-- Schemes Client/Server/Facilitator interfaces per scheme
|-- HTTP Encode/decode for HTTP headers (base64 JSON)
|-- Compat Optional x402 migration aid
|-- Errors Typed error codes with recovery hints
|
@sweefi/sui <-- Sui adapter: 40 PTB builders + SuiPaymentAdapter + createS402Client
@sweefi/server <-- Chain-agnostic HTTP: s402Gate middleware + wrapFetchWithS402
@sweefi/ui-core <-- State machine + PaymentAdapter interface
@sweefi/vue <-- Vue 3 plugin + useSweefiPayment() composable
@sweefi/react <-- React context + useSweefiPayment() hooks402 is chain-agnostic protocol plumbing. It defines what gets sent over HTTP. The Sui-specific how lives in @sweefi/sui.
Payment Schemes
Exact (v0.1)
One-shot payment. Client builds a signed transfer PTB, facilitator verifies + broadcasts atomically.
Client Server Facilitator
|--- GET /api/data ------->| |
|<-- 402 + requirements ---| |
| | |
| (build PTB, sign) | |
|--- GET + x-payment ----->|--- verify + settle ---->|
| |<--- { success, tx } ----|
|<-- 200 + data -----------| |This is the x402-compatible baseline. An x402 client can talk to an s402 server using this scheme with zero modifications.
Prepaid (v0.1)
Deposit-based access. Agent deposits funds into an on-chain Balance shared object targeted at a specific provider. API calls happen off-chain. Provider batch-claims accumulated usage. Move module enforces rate caps — no trust required.
Phase 1 (deposit — one on-chain TX):
Agent deposits 10 SUI → Balance shared object created
Gas: ~$0.007
Phase 2 (usage — off-chain, zero gas):
Agent makes 1,000 API calls
Server tracks usage, no on-chain TX per call
Phase 3 (claim — one on-chain TX):
Provider claims accumulated $1.00 from Balance
Gas: ~$0.007
─────────────────────────────────────────────────────
Total gas: $0.014 for 1,000 calls
Per-call effective gas: $0.000014This is the agent-native payment pattern. Without prepaid, per-call settlement costs $7.00 in gas for $1.00 of API usage (economically impossible). With prepaid, it costs $0.014 (economically trivial).
Use cases: AI agent API budgets, high-frequency API access, compute metering.
Escrow (v0.1)
Time-locked vault with arbiter dispute resolution. Full state machine: ACTIVE -> DISPUTED -> RELEASED / REFUNDED.
- Buyer deposits funds, locked until release or deadline
- Buyer confirms delivery -> funds release to seller (receipt minted)
- Deadline passes -> permissionless refund (anyone can trigger)
- Either party disputes -> arbiter resolves
Use cases: digital goods delivery, freelance payments, trustless commerce.
Unlock
Pay-to-decrypt encrypted content. Escrow + encrypted content delivery. The buyer pays into escrow; on release, the EscrowReceipt unlocks encrypted content stored on Walrus. Currently powered by Sui SEAL.
This scheme depends on encryption key server infrastructure and is under active development.
Stream
Per-second micropayments via on-chain StreamingMeter. Client deposits funds into a shared object; recipient claims accrued tokens over time.
Phase 1 (402 exchange):
Client builds stream creation PTB --> facilitator broadcasts
Result: StreamingMeter shared object on-chain
Phase 2 (ongoing access):
Client includes x-stream-id header --> server checks on-chain balance
Server grants access as long as stream has fundsUse cases: AI inference sessions, video streaming, real-time data feeds.
Quick Start
Types only (most common)
import type {
s402PaymentRequirements,
s402PaymentPayload,
s402SettleResponse,
} from 's402';HTTP header encoding
import {
encodePaymentRequired,
decodePaymentRequired,
encodePaymentPayload,
decodePaymentPayload,
detectProtocol,
} from 's402';
// Server: build 402 response
const requirements: s402PaymentRequirements = {
s402Version: '1',
accepts: ['exact', 'stream'],
network: 'sui:mainnet',
asset: '0x2::sui::SUI',
amount: '1000000', // 0.001 SUI in MIST
payTo: '0x0000000000000000000000000000000000000000000000000000000000000001',
};
response.status = 402;
response.headers.set('payment-required', encodePaymentRequired(requirements));
// Client: read 402 response
const header = response.headers.get('payment-required')!;
const reqs = decodePaymentRequired(header);
console.log(reqs.accepts); // ['exact', 'stream']
console.log(reqs.amount); // '1000000'x402 compat (opt-in)
import {
normalizeRequirements,
isS402,
isX402,
toX402Requirements,
fromX402Requirements,
} from 's402/compat';
// Normalize x402 JSON (V1 or V2) to s402 format
const requirements = normalizeRequirements(rawJsonObject);
// Convert s402 -> x402 V1 for legacy clients
const x402Reqs = toX402Requirements(requirements);Error handling
import { s402Error, s402ErrorCode } from 's402';
try {
await facilitator.settle(payload, requirements);
} catch (e) {
if (e instanceof s402Error) {
console.log(e.code); // 'INSUFFICIENT_BALANCE'
console.log(e.retryable); // false
console.log(e.suggestedAction); // 'Top up wallet balance...'
}
}Scheme registry (client)
import { s402Client } from 's402';
const client = new s402Client();
// Register scheme implementations (from @sweefi/sui or your own)
client.register('sui:mainnet', exactScheme);
client.register('sui:mainnet', streamScheme);
// Auto-selects best scheme from server's accepts array
const payload = await client.createPayment(requirements);Scheme registry (facilitator)
import { s402Facilitator } from 's402';
const facilitator = new s402Facilitator();
facilitator.register('sui:mainnet', exactFacilitatorScheme);
// Atomic verify + settle
const result = await facilitator.process(payload, requirements);
if (result.success) {
console.log(result.txDigest); // Sui transaction digest
}Sub-path Exports
import { ... } from 's402'; // Everything
import type { ... } from 's402/types'; // Types + constants only
import { ... } from 's402/http'; // HTTP encode/decode
import { ... } from 's402/compat'; // x402 interop
import { ... } from 's402/errors'; // Error typesImplementing a Scheme
s402 is designed as a plugin system. Each payment scheme implements three interfaces:
import type {
s402ClientScheme, // Client: build payment payload
s402ServerScheme, // Server: build payment requirements
s402FacilitatorScheme, // Facilitator: verify + settle
s402DirectScheme, // Optional: settle without facilitator
} from 's402';The reference Sui implementation of all five schemes is available in @sweefi/sui.
Wire Format
s402 uses the same HTTP headers as x402 V1:
| Header | Direction | Content |
|--------|-----------|---------|
| payment-required | Server -> Client | Base64-encoded s402PaymentRequirements JSON |
| x-payment | Client -> Server | Base64-encoded s402PaymentPayload JSON |
| payment-response | Server -> Client | Base64-encoded s402SettleResponse JSON |
Note: x402 V2 renamed the client payment header to
payment-signature. s402 usesx-payment(matching x402 V1). All header names are lowercase per HTTP/2 (RFC 9113 §8.2.1). x402 V2 servers accept both headers, so s402 clients work with both versions. If your server needs to accept x402 V2 clients, also checkpayment-signature.
The presence of s402Version in the decoded JSON distinguishes s402 from x402. Clients and servers can auto-detect the protocol using detectProtocol().
Discovery
Servers can advertise s402 support at /.well-known/s402.json:
{
"s402Version": "1",
"schemes": ["exact", "stream", "escrow", "unlock", "prepaid"],
"networks": ["sui:mainnet"],
"assets": ["0x2::sui::SUI", "0xdba...::usdc::USDC"],
"directSettlement": true,
"mandateSupport": true,
"protocolFeeBps": 50
}Security
HTTPS is required. s402 payment data (requirements, payloads, settlement responses) travels in HTTP headers as base64-encoded JSON. Without TLS, this data is visible to any network observer. All production deployments MUST use HTTPS.
Requirements expiration. Servers SHOULD set expiresAt on payment requirements to prevent replay of stale 402 responses. The facilitator rejects expired requirements before processing.
const requirements: s402PaymentRequirements = {
s402Version: '1',
accepts: ['exact'],
network: 'sui:mainnet',
asset: '0x2::sui::SUI',
amount: '1000000',
payTo: '0x0000000000000000000000000000000000000000000000000000000000000001',
expiresAt: Date.now() + 5 * 60 * 1000, // 5-minute window
};Design Principles
Protocol-agnostic core, Sui-native reference.
s402defines chain-agnostic protocol types and HTTP encoding. The reference implementation (@sweefi/sui, coming soon) will exploit Sui's unique properties — PTBs, object model, sub-second finality. Other chains can implement s402 schemes using their own primitives.Optional x402 compat. The
s402/compatsubpath provides a migration aid for codebases with x402-formatted JSON. It normalizes x402 V1 (maxAmountRequired) and V2 (amount) to s402 format. This is opt-in — the core protocol has no x402 dependency.Scheme-specific verification. Each scheme has its own verify logic. Exact verify (signature recovery + dry-run) is fundamentally different from stream verify (deposit check + rate validation). The facilitator dispatches — it doesn't share logic.
Zero runtime dependencies.
s402is pure TypeScript protocol definitions. No Sui SDK, no crypto libraries, no HTTP framework. Chain-specific code belongs in adapters.Errors tell you what to do. Every error code includes
retryable(can the client try again?) andsuggestedAction(what should it do?). Agents can self-recover.
Conformance Testing
s402 ships 133 machine-readable JSON test vectors for cross-language conformance. If you're implementing s402 in Go, Python, Rust, or any other language, use these vectors to verify your implementation matches the spec.
# Vectors are in the npm package
ls node_modules/s402/test/conformance/vectors/
# Or clone the repo
ls test/conformance/vectors/See test/conformance/README.md for the vector format, encoding scheme, and implementation guide.
Related
- SweeFi — Open-source payment SDK built on s402. 10 packages including PTB builders, MCP tools, CLI, and UI components.
- Sui Gas Station — Sponsored transaction infrastructure for Sui.
License
Apache-2.0 — see LICENSE for details.
