Skip to content

TypeScript API reference

Two SubEtha packages are published to npm; both are Apache-2.0, ESM-only, and require Node.js >= 20. Everything below is the surface actually exported by the package entry points at the pinned product commit — nothing more.

applies to @subetha/x402-scheme 0.1.0 (npm, verified 2026-07-27)applies to @subetha/provider 0.1.0 (npm, verified 2026-07-27)

The facilitator is not on this list on purpose: @subetha/facilitator is private/workspace-internal and is operated from the product repository source only (npm returns 404 for it — see Versions & compatibility).

@subetha/x402-scheme — payer-side scheme, types, fees

Section titled “@subetha/x402-scheme — payer-side scheme, types, fees”
Terminal window
npm install @subetha/x402-scheme @x402/core

@x402/core (^2.17.0) is a peer dependency; viem (^2.21.0) is installed automatically as a regular dependency. The package entry re-exports three modules (src/index.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
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
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 fail-fasts when an offer lacks extra.helper — it never silently degrades to a gas-paying transfer. Construction requires rpcUrl or an injected publicClient.

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
const scheme = new SubethaSchemeClient({ account, mode: "permit", rpcUrl });
const client = new x402Client().register("eip155:31337", scheme);
const fetchWithPay = wrapFetchWithPayment(fetch, client);

@subetha/provider — provider-side scheme server and handler

Section titled “@subetha/provider — provider-side scheme server and handler”
Terminal window
npm install @subetha/provider @x402/core

@x402/core (^2.17.0) is a peer dependency; @subetha/x402-scheme is installed automatically as a regular dependency. The package entry re-exports four modules (src/index.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.

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
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
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 kind
app.use(paymentMiddleware({ "/api/*": {
accepts: subethaAccepts(scheme, { network: "eip155:31337", price: "1000" }),
} }, server));

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.