InFlow's Node.js SDK for the x402 protocol. Add InFlow to your existing foundation V2
middleware as a facilitator, generate your route's accepts[] from your seller config, and accept or make x402 payments
— InFlow balance transfers, on-chain exact-amount transfers, and (forthcoming) instrument-based payments.
| Package | Role | Install when… |
|---|---|---|
@inflowpayai/x402 |
Core types + HTTP client | Rarely installed directly. |
@inflowpayai/x402-seller |
Facilitator client + seller client + inflowAccepts helper |
Accepting x402 payments as a seller. |
@inflowpayai/x402-buyer |
InflowClient — foundation x402Client subclass for buyers |
Paying via x402, with or without on-chain signers. |
All packages publish under the @inflowpayai scope and depend on @x402/core@^2.22.0 as a peer.
The SDK does not ship a seller middleware. Sellers use the foundation V2 middleware (paymentMiddlewareFromConfig
from @x402/express, @x402/hono, @x402/fastify, or @x402/next) directly and pass the InFlow facilitator into its
facilitatorClients array. See architecture.md for the rationale.
Create an InFlow Seller account and API key in the environment you will use: sandbox
for testing or production for live payments. A Developer account does not authorize
/v1/x402/config; the SDK surfaces the server's SELLER_ACCOUNT_REQUIRED error when the supplied credential has the
wrong account type.
The InFlow buyer client requires an authenticated InFlow account. For a buyer-only API-key integration, create a
Developer account. If the application already has a Seller account, reuse it; Seller accounts can buy. Create the
account and credential in sandbox for testing or production
for live payments, then pass the matching environment to the SDK. Foundation-only EVM and SVM buyers use their own
keys and do not require an InFlow account.
pnpm add @inflowpayai/x402-seller @x402/express @x402/core expressimport { paymentMiddlewareFromConfig } from '@x402/express';
import express from 'express';
import {
createInflowFacilitator,
createInflowSellerClient,
inflowAccepts,
inflowSchemeRegistrations,
} from '@inflowpayai/x402-seller';
const apiKey = process.env.INFLOW_API_KEY!;
const inflow = createInflowFacilitator({ environment: 'sandbox', apiKey });
const client = await createInflowSellerClient({ environment: 'sandbox', apiKey });
const app = express();
app.use(express.json());
app.use(
paymentMiddlewareFromConfig(
{
'GET /api/widgets': {
accepts: await inflowAccepts(client, { price: '$0.01' }),
},
'POST /api/upload': {
accepts: await inflowAccepts(client, {
price: '0.10 USDC',
schemes: ['balance', 'exact'],
}),
},
},
[inflow],
await inflowSchemeRegistrations(client),
),
);
app.get('/api/widgets', (_req, res) => res.json({ widgets: [1, 2, 3] }));
app.listen(3000);For Hono, swap @x402/express for @x402/hono; everything else is the same. For Fastify, use @x402/fastify — its
paymentMiddlewareFromConfig mutates the Fastify instance in place rather than returning a middleware function
(paymentMiddlewareFromConfig(app, routes, [inflow], await inflowSchemeRegistrations(client))). For Next 16, use
@x402/next's paymentProxyFromConfig from a root-level proxy.ts file (Next 16 renamed the convention from
middleware.ts); see examples/x402-seller-next for the complete shape including
the proxy.ts placement, route-handler structure, and the required next pin (~16.2.6, to match
@x402/next@2.22.0's peer range).
The pieces:
createInflowFacilitatorreturns a foundationFacilitatorClient(verify/settle/getSupported). Drops intopaymentMiddlewareFromConfig'sfacilitatorClientsarray. Authed —apiKeyis required at the type level.createUnauthenticatedInflowFacilitatoris the explicit escape hatch for facilitator-only deployments (self-hosted, public-facilitator mode, test harnesses).createInflowSellerClientowns the seller-authed/v1/x402/configendpoint plus signer-address discovery. DrivesinflowAccepts. Async factory — primes its caches in parallel before resolving.inflowAccepts(client, options)expands the seller's config into foundationPaymentOption[](asset contract + atomic amount pre- resolved). Splat into a route'sacceptsarray.inflowSchemeRegistrations(client)returns the passthroughSchemeRegistration[]covering every(scheme, network)and asset transfer method the seller's config can emit. Each registration preserves the authorization flow (verify before the handler, settle after it). Pass through the adapter'sschemesargument; the foundation refuses to boot otherwise.
pnpm add @inflowpayai/x402-buyer @x402/coreimport { createInflowClient } from '@inflowpayai/x402-buyer';
import { x402HTTPClient } from '@x402/core/client';
const core = await createInflowClient({
apiKey: process.env.INFLOW_API_KEY!,
environment: 'sandbox',
});
const http = new x402HTTPClient(core);
const initial = await fetch('https://api.example.com/widgets');
if (initial.status === 402) {
const paymentRequired = http.getPaymentRequiredResponse((n) => initial.headers.get(n));
const paymentPayload = await http.createPaymentPayload(paymentRequired);
const paymentHeaders = http.encodePaymentSignatureHeader(paymentPayload);
const paid = await fetch('https://api.example.com/widgets', { headers: paymentHeaders });
const result = await http.processResponse(paid);
if (result.kind === 'success') {
console.log(result.body);
}
}InflowClient extends @x402/core's x402Client, so foundation registration helpers slot in directly when the buyer
wants to pay non-InFlow networks as well:
import { registerExactEvmScheme } from '@x402/evm/exact/client';
import { registerExactSvmScheme } from '@x402/svm/exact/client';
registerExactEvmScheme(core, { signer: evmAccount, networks: ['eip155:1'] });
registerExactSvmScheme(core, { signer: svmKeypair, networks: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'] });When the seller's 402 offers a requirement InFlow can sign (balance/inflow:1, or any (scheme, network) advertised by
the buyer capability cache), the InFlow path wins. Otherwise the foundation's selector routes to whatever EVM or SVM
scheme the caller registered above — same client, two paths.
paymentMiddlewareFromConfig({/* routes */}, [
inflow,
cdp, // e.g. Coinbase CDP facilitator
polygon, // any other FacilitatorClient
]);First claimer in the facilitatorClients array of a (scheme, network) pair (via getSupported()) wins verify/settle
routing — that's the foundation middleware's resolution contract. Order the array intentionally; subsequent claimers are
silently ignored.
On the buyer side, InflowClient enforces a fixed precedence instead: the InFlow buyer capability cache is consulted
first, and only the foundation's registered schemes get a turn when nothing matches. This mirrors the seller-side rule
that an InFlow facilitator placed first wins on its claimed pairs.
The SDK supports three payment schemes:
balance— InFlow-internal ledger transfer between two InFlow accounts. No on-chain transaction, no gas. The fastest path; uses the literal'inflow:1'network identifier.exact— on-chain transfer signed via EIP-3009 or Permit2 (EVM) or the chain-specific signing method (Solana, Aptos, Stellar). Uses CAIP-2 network identifiers —eip155:<chainId>for EVM (e.g.eip155:8453); for Solana, the spec-strictsolana:<first-32-base58-chars-of-genesis-hash>(e.g.solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpfor mainnet).instrument— reserved. The value is in the type union for forward compatibility;inflowAcceptspasses it through unchanged if a server ever publishes it, but end-to-end settlement support is not yet enabled.
See protocol-mapping.md for how each scheme maps to the on-the-wire PaymentRequirements /
PaymentOption shapes.
The payment-identifier extension is supported end-to-end. The SDK validates the format client-side; callers opt in
with their own ID via SignOptions.paymentId on prepareInflowPayment. New extensions plug in as single-file handlers
— see extensions.md.
Per-route extension declarations live on RouteConfig.extensions (foundation middleware). Facilitator-wide declarations
come from each FacilitatorClient.getSupported().extensions and are merged by the middleware automatically.
- architecture.md — what the SDK contributes vs. what the foundation owns, conflict precedence, request lifecycle.
- protocol-mapping.md — InFlow ↔ wire types, network identifier rules, decimal sourcing.
- extensions.md — the
payment-identifierextension and the handler contract for new extensions.
For monorepo-level docs (publishing, contributing, tooling), see ../monorepo.
MIT.