Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

SDK

x402 payments

x402 is an HTTP 402 flow: a server answers 402 with payment requirements, the client signs an authorization, base64-encodes it into an X-PAYMENT header, and retries. A facilitator settles the authorization on-chain. fetchWithX402 does this transparently from a session key — the agent just calls a URL.

Both supported rails verify the session key on-chain via ERC-1271:

  • permit2-exact (the reliable rail — any token approved to Permit2). Includes Binance B402, which binds the recipient with a Permit2 witness (permitWitnessTransferFrom). Checker = Permit2.
  • exact / EIP-3009 (the standard x402 wire). Only works for tokens whose EIP-3009 is ERC-1271-aware (Circle FiatTokenV2_2, e.g. Base/Ethereum USDC). Checker = the token.

Pay for a resource

import { createClient, BNB } from "@altananetwork/sdk";
 
const client = createClient({ chains: [BNB] });
// `session` from grantSession; the wallet must have approved Permit2 + the checker (below).
 
const res = await client.fetchWithX402({
  session,
  url: "https://api.example.com/paid-endpoint",
  // chainId defaults to the client's default chain; override to target another.
});
 
console.log(res.status, await res.text()); // 200 + paid content

One-time provisioning

Before the first payment, the wallet's admin sets up the rail:

import { PERMIT2_ADDRESS } from "@altananetwork/sdk";
 
// permit2-exact rail:
await client.approveTokenForPermit2({ wallet, signer: admin, token: USDC });      // ERC20 approve(Permit2)
await client.approveSignatureChecker({ wallet, signer: admin, session, checker: PERMIT2_ADDRESS });
 
// EIP-3009 rail (checker = the token itself):
await client.approveSignatureChecker({ wallet, signer: admin, session, checker: USDC });

See approveTokenForPermit2 and approveSignatureChecker.

Parameters

client.fetchWithX402(opts: ClientFetchWithX402Options): Promise<Response>;
 
type ClientFetchWithX402Options = {
  session: Session;
  url: string;
  init?: RequestInit;
  /** Only pay options on this chain when any match. Defaults to the client's chain. */
  chainId?: number;
  /** Preferred rail when a chain offers several. Defaults to "permit2". */
  preferRail?: "permit2" | "eip3009";
};

Non-402 responses pass through untouched; a 402 is parsed, the best payable option is selected (preferring the requested chain, then the permit2 rail), signed, and retried.

Lower-level helpers

For servers/facilitators or custom flows, the standalone functions are also exported:

import {
  fetchWithX402,          // fetchWithX402(session, url, init?, { chainId, preferRail })
  selectX402Requirement,  // choose an option from a 402 `accepts[]`
  signX402Payment,        // sign one requirement → { header, payload }
  buildPermit2TypedData,        // plain PermitTransferFrom
  buildPermit2WitnessTypedData, // permit2-exact PermitWitnessTransferFrom (B402)
  buildEip3009TypedData,        // TransferWithAuthorization
  encodeXPaymentHeader,
  networkToChainId,             // CAIP-2 "eip155:56" → 56
  normalizeResource,            // 402 `resource` (object or URL string) → { url, ... }
  PERMIT2_ADDRESS,
} from "@altananetwork/sdk";

B402 wire compatibility

Real b402 merchants (CoinMarketCap and the BNB Agent Studio ecosystem) read a few envelope fields under different names than the plain x402 sample does. fetchWithX402 and signX402Payment emit both dialects, so the same payment is readable by b402 merchants and by anything already integrated against Altana:

  • resource. The envelope carries a top-level resource object saying what the payment buys, echoed from the 402 body. Merchants reject an envelope without it: CoinMarketCap answers payment header resource is null. fetchWithX402 carries it across automatically, accepting either the object form or a bare URL string, and falling back to the requested URL.
  • permit2Authorization. Permit2 payloads carry the authorization twice: under permit with a sibling from (the Altana dialect), and under permit2Authorization with from nested inside (the b402 dialect). Same values, one signature.
  • PAYMENT-SIGNATURE. The envelope is sent under both X-PAYMENT and PAYMENT-SIGNATURE, since some b402 merchants read only the latter.

When passing a requirement to signX402Payment yourself, set resource on it so the envelope carries one.

Notes

  • Browser limitation. Third-party x402 endpoints often omit X-PAYMENT from CORS Access-Control-Allow-Headers, so a browser can't POST the payment. Run fetchWithX402 server-side.
  • The signature is a 98-byte ERC-1271 envelope, not an EOA signature — see Off-chain signatures. A facilitator must verify via isValidSignature.