TypeScript API reference
Two SubEtha packages are published to npm; both are Apache-2.0, ESM-only, and
require Node.js >= 20. The sections marked published 0.1.0 surface describe
what was verified in the registry package. The later source-additions sections
are based on the merged product source at 85fd50ac; they are not asserted to be
present in the registry artifact until a new npm publication is independently
verified. The exact official x402 package pins used below were manually checked
against npm on 2026-08-07; that auxiliary observation is recorded in the
repository’s registry-verification evidence.
@subetha/x402-scheme 0.1.0 (npm, verified 2026-08-07)applies to @subetha/provider 0.1.0 (npm, verified 2026-08-07)
The facilitator daemon is not on this list on purpose: subetha-facilitator
is the private/workspace-internal package under apps/facilitator; the separate
@subetha/facilitator workspace library is also not published to npm (npm
returns 404 for it — see Versions & compatibility).
Both are operated from product repository source only.
@subetha/x402-scheme — payer-side scheme, types, fees
Section titled “@subetha/x402-scheme — payer-side scheme, types, fees”The published 0.1.0 registry artifact’s documented surface contains three
modules. The current source index below is cited for source reconciliation and
re-exports five modules at 85fd50ac; its two additional history modules are
listed separately as source additions and are not asserted as present in the
published artifact.
(src/index.ts)
Constants and network helpers (types.ts)
Section titled “Constants and network helpers (types.ts)”| export | kind | description |
|---|---|---|
SUBETHA_SCHEME |
const |
the scheme identifier, "subetha-zerc20" |
DEFAULT_EXPIRY_SKEW_SECONDS |
const |
clock-skew margin (5): clients treat expiresAt <= now + skew as expired |
DEFAULT_LAST_BROADCAST_CUTOFF_SECONDS |
const |
self-transfer safety margin (30): refuse to broadcast a burn this close to expiry |
MAX_REASONABLE_EPOCH_SECONDS |
const |
1e11 — values at/above this cannot be unix seconds (milliseconds guard) |
toNetwork(chainId) |
function | CAIP-2 id for an EVM chain id (eip155:<chainId>) |
parseNetwork(network) |
function | parse a CAIP-2 eip155 id back to a chain id; throws on non-eip155 input |
Wire types and validation (types.ts)
Section titled “Wire types and validation (types.ts)”| export | kind | description |
|---|---|---|
SubethaFeeInfo |
interface | fee disclosure { amount, bps } (payer-charged mode only; already included in amount) |
SubethaExtra |
interface | the scheme’s PaymentRequirements.extra: challengeNonce, expiresAt, helper?, deadline?, fee? |
SubethaPermitPayload |
interface | gasless payload: challengeNonce, payer, permit { value, nonce, deadline, v, r, s }, burnAuthSig |
SubethaSelfTransferPayload |
interface | fallback payload: challengeNonce, burnTxHash |
parseSubethaRequirements(requirements) |
function | validate + narrow an offer’s SubEtha fields (payTo, asset, amount, chainId, extra); throws a specific message on anything malformed |
Payer client (client.ts)
Section titled “Payer client (client.ts)”| export | kind | description |
|---|---|---|
SubethaSchemeClientOptions |
interface | account (required), mode? ("permit" default | "self-transfer"), rpcUrl?, publicClient?, walletClient?, expirySkewSeconds?, lastBroadcastCutoffSeconds? |
SubethaSchemeClient |
class | x402 v2 scheme handler for subetha-zerc20 (structurally implements the official SchemeNetworkClient). Members: scheme, createPaymentPayload(x402Version, requirements), burnTxFor(challengeNonce) (self-transfer mode), lastRequirements |
In the default "permit" mode the client fails fast when an offer lacks
extra.helper — it never silently degrades to a gas-paying transfer.
Construction requires rpcUrl or an injected publicClient.
Fee interpretation (fee.ts)
Section titled “Fee interpretation (fee.ts)”| export | kind | description |
|---|---|---|
ParsedFee |
interface | { fee, bps, quoted, listPrice } (all amounts as bigint base units) |
parseFee(requirements) |
function | typed view of an offer’s fee disclosure; undefined when the offer carries no fee field |
feeAwarePolicy() |
function | a PaymentPolicy for the official x402 client that ranks offers by the quoted total (the payer’s true spend), never by list price |
Current source additions (not asserted as published npm surface)
Section titled “Current source additions (not asserted as published npm surface)”The merged SubEtha source at 85fd50ac additionally re-exports the following
history-related helpers from @subetha/x402-scheme:
| export | source | role |
|---|---|---|
HistoryBinding, createHistoryBinding(), verifyHistoryBinding() |
src/history-binding.ts |
bind an offer and idempotency key to a history record |
PayerHistoryStatus, PayerHistoryAttestation, verifyPayerHistoryAttestation() |
src/history-attestation.ts |
verify a facilitator proof before exposing a payer claim |
These source exports must not be treated as available from the currently
published 0.1.0 artifact until npm publication is re-verified.
Payer wiring (from the protocol spec)
Section titled “Payer wiring (from the protocol spec)”const scheme = new SubethaSchemeClient({ account, mode: "permit", rpcUrl });const client = new x402Client().register("eip155:31337", scheme);const fetchWithPay = wrapFetchWithPayment(fetch, client);Import x402Client from @x402/core/client and wrapFetchWithPayment from
@x402/fetch. The provider example imports x402ResourceServer from
@x402/core/server and paymentMiddleware from @x402/express; these helpers
are not exports of the SubEtha packages. The examples use ESM imports, matching
the published packages’ ESM-only runtime contract:
import { x402Client } from "@x402/core/client";import { wrapFetchWithPayment } from "@x402/fetch";@subetha/provider — provider-side scheme server and handler
Section titled “@subetha/provider — provider-side scheme server and handler”The published 0.1.0 registry artifact’s documented surface contains four
modules. The current source index below is cited for source reconciliation and
re-exports five modules at 85fd50ac; its additional history client is listed
separately as a source addition and is not asserted as present in the published
artifact.
(src/index.ts)
Facilitator HTTP client (facilitator-client.ts)
Section titled “Facilitator HTTP client (facilitator-client.ts)”| export | kind | description |
|---|---|---|
SubethaChallengeOffer |
interface | the facilitator’s /challenge response: payTo, amount, token, chainId, challengeNonce, expiresAt, helper?, deadline?, fee? |
SubethaFacilitatorClient |
class | implements the official FacilitatorClient over HTTP: verify(…), settle(…), getSupported(), plus the SubEtha extension createChallenge(priceBaseUnits) |
The facilitator is reached as an external HTTP service — this package includes no facilitator runtime or backend and does not enable launching one.
Payment handler (handler.ts)
Section titled “Payment handler (handler.ts)”| export | kind | description |
|---|---|---|
HandlerResult |
interface | what a resource handler returns instead of writing to res: { body, status?, contentType? } |
SubethaHandlerResponse |
interface | the structural slice of express.Response the wrapper drives (no express dependency) |
subethaHandler(fn) |
function | wraps a return-value handler into an Express-compatible route handler with exactly one response commit; a thrown handler becomes a 500 and the payment is not settled |
| export | kind | description |
|---|---|---|
SUBETHA_PLACEHOLDER_PAYTO |
const |
fallback payTo for static quote-only route configs; real payment routes must use subethaAccepts |
SubethaSchemeServerOptions |
interface | maxCachedOffers? (default 5000), clock?, feePolicy? |
SubethaSchemeServer |
class | structurally implements the official SchemeNetworkServer; mints one fresh burn-address offer per unpaid 402 via the facilitator and caches live offers in memory (single-process assumption) |
subethaAccepts(schemeServer, { network, price }) |
function | builds a route’s accepts entry whose dynamic payTo mints per 402 and echoes the client’s accepted.payTo on the paying request |
Offer validation (validate.ts)
Section titled “Offer validation (validate.ts)”| export | kind | description |
|---|---|---|
EXPIRY_SKEW_SECONDS |
const |
re-export of the shared clock-skew margin |
OfferBinding |
interface | the identity an offer is bound to |
SubethaFeePolicy |
interface | the facilitator’s non-secret fee policy as the provider expects it |
validateFeePolicy(policy) |
function | fail-fast structural validation of a fee policy |
SubethaOfferEntry |
interface | a fully-built cached offer entry |
canonicalizePathname(path) |
function | path canonicalization for offer binding |
bindingsEqual(a, b) |
function | offer-binding equality |
validateChallengeOffer(…) |
function | validates a facilitator challenge offer against the expected binding and fee policy |
validateBuiltRequirements(…) |
function | validates the requirements object built from an offer |
Current source addition (not asserted as published npm surface)
Section titled “Current source addition (not asserted as published npm surface)”The merged source also re-exports ProviderHistoryRecord and
ProviderHistoryClient from src/history-client.ts. This client binds provider
offers to idempotency keys and can acknowledge a resource or accept a finalized
claim. It is a source-level addition at 85fd50ac, not a claim that the published
@subetha/[email protected] artifact contains it.
Provider wiring (from the protocol spec)
Section titled “Provider wiring (from the protocol spec)”const facilitator = new SubethaFacilitatorClient(FACILITATOR_URL);const scheme = new SubethaSchemeServer(facilitator);const server = new x402ResourceServer([facilitator]);server.register("eip155:31337", scheme);await server.initialize(); // hard-fails unless /supported advertises the kindapp.use(paymentMiddleware({ "/api/*": { accepts: subethaAccepts(scheme, { network: "eip155:31337", price: "1000" }),} }, server));FACILITATOR_URL is the separately operated facilitator HTTP base URL.
eip155:31337 is the local development-chain example, not a protocol-wide
network restriction. The provider configures its payout address but does not
need to hold a signing key for this wiring.
The corresponding x402 imports are:
import { x402ResourceServer } from "@x402/core/server";import { paymentMiddleware } from "@x402/express";price is the list price in base units; the quoted amount (with any
fee) comes from the facilitator per request. The provider holds no keys except
its payout EOA and never touches the chain or the zERC20 SDK.
Related pages
Section titled “Related pages”- Wire protocol reference — the formats these APIs produce and consume.
- Configuration reference — the
SUBETHA_*payer environment. - Python API — the PyPI payer package.