@optimai-network/x402-sdk
v0.3.0
Published
Typed client SDK for OptimAI x402-paid search endpoints.
Readme
@optimai-network/x402-sdk
Typed client SDK for the api-server-onchain x402 search endpoints.
It wraps the two awkward parts of the flow:
- the initial
402 Payment Requiredchallenge - the follow-up
payment-signatureheader generation
It also returns a serializable paymentContext object, including the original payment-signature, so another agent or process can keep working with the same paid search later.
Install
Requires Node.js 20.18.0 or newer.
pnpm add @optimai-network/x402-sdkQuick start: EVM / Base
import {
createOptimaiX402Client,
createViemPaymentHandler,
} from "@optimai-network/x402-sdk";
const paymentHandler = createViemPaymentHandler({
privateKey: process.env.X402_EVM_PAYER_PRIVATE_KEY!,
rpcUrls: {
"eip155:84532": "https://sepolia.base.org",
},
});
const client = createOptimaiX402Client({
baseUrl: "http://localhost:3002",
paymentHandler,
});
const { search, paymentContext } = await client.createSearch({
query: "What is liquidation?",
});
const completed = await client.waitForSearchCompletion(search.id, {
paymentContext,
});
console.log(completed.result?.answer);Quick start: Solana
import {
createOptimaiX402Client,
createSolanaPaymentHandler,
} from "@optimai-network/x402-sdk";
const paymentHandler = await createSolanaPaymentHandler({
privateKey: process.env.X402_SOLANA_PAYER_PRIVATE_KEY!,
rpcUrls: {
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp":
process.env.X402_SOLANA_MAINNET_RPC_URL ?? "https://api.mainnet-beta.solana.com",
},
});
const client = createOptimaiX402Client({
baseUrl: "https://api-onchain.optimai.network",
paymentHandler,
});
const { search, paymentContext } = await client.createSearch({
query: "What is liquidation?",
});
const completed = await client.waitForSearchCompletion(search.id, {
paymentContext,
});
console.log(completed.result?.answer);Payment requirements and payTo
SDK users do not manually set payTo, amount, asset, network, or Solana feePayer.
Those values come from the server's 402 Payment Required challenge, and the selected payment handler signs one compatible offer from that challenge.
If your product needs to verify the receiver, inspect paymentContext.paymentRequired.accepts and confirm the selected offer's payTo matches the merchant wallet you expect before continuing.
API
createOptimaiX402Client(config)
Creates the high-level SDK client.
Methods:
createSearch(input, options?)getSearch(id, options?)cancelSearch(id, options?)waitForSearchCompletion(id, options?)rememberPaymentContext(context)forgetPaymentContext(id)
createViemPaymentHandler(config)
Creates a payment handler backed by @x402/evm, @x402/fetch, and viem.
Built-in network support:
eip155:8453-> Baseeip155:84532-> Base Sepolia
When a challenge includes multiple accepts, this handler selects the first EVM offer.
createSolanaPaymentHandler(config)
Creates a payment handler backed by @x402/svm for Solana/SVM x402 payments.
import {
createOptimaiX402Client,
createSolanaPaymentHandler,
} from "@optimai-network/x402-sdk";
const paymentHandler = await createSolanaPaymentHandler({
privateKey: process.env.X402_SOLANA_PAYER_PRIVATE_KEY!, // base58 or JSON-encoded 64-byte secret key
rpcUrls: {
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": "https://api.mainnet-beta.solana.com",
},
});
const client = createOptimaiX402Client({
baseUrl: "https://api-onchain.optimai.network",
paymentHandler,
});Built-in network support:
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp-> Solana mainnetsolana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1-> Solana devnet
When a challenge includes multiple accepts, this handler selects the first Solana offer. The server challenge must include a facilitator-managed extra.feePayer; the SDK payer key only signs as the token owner.
Base vs Solana
Both payment handlers use the same createOptimaiX402Client API. The difference is only how the wallet signs the x402 payment:
- EVM / Base uses
createViemPaymentHandler, a 32-byte hex private key, Base USDC, and Base gas. - Solana uses
createSolanaPaymentHandler, a base58 or JSON-encoded 64-byte Solana secret key, SPL USDC, and SOL for network fees. - EVM addresses are
0x...; Solana addresses are base58 public keys. - On Solana,
extra.feePayeris facilitator-managed and comes from the server challenge. It is not the SDK user's payer wallet.
x402 payment status meanings
x402_payment_status in responses can be:
settlement_unconfirmed: payment is verified, but settlement is not finalized in OptimAI yet. Chain transfer may already exist while the server is still confirming/persisting settlement id.settled: settlement is finalized and settlement id (tx hash) is persisted.voided: payment was voided (usually request cancelled/failed before settlement).settlement_failed: server exhausted settlement retries and needs manual/operator follow-up.
Persisting search access across agents
GET /external/v1/x402/search/:id works best when the client replays the exact original payment-signature used for the paid create call.
Because of that, the SDK returns:
type SearchPaymentContext = {
id: string;
paymentRequired: X402PaymentRequired;
paymentSignature?: string;
};Persist this object alongside the search id if another agent or process will poll or fetch the search later:
const { search, paymentContext } = await client.createSearch({ query: "..." });
// store paymentContext somewhere durable
client.rememberPaymentContext(paymentContext);
const latest = await client.getSearch(search.id);If you retry createSearch() with the same Idempotency-Key from another process, pass the previously stored context back in options.existingPaymentContext so the SDK can authenticate the 303 redirect target with the original signature:
const retry = await client.createSearch(
{ query: "..." },
{
idempotencyKey: "search-123",
existingPaymentContext: paymentContext,
},
);Local smoke tests
The package includes separate smoke scripts for EVM and Solana payment handlers.
EVM:
X402_EVM_PAYER_PRIVATE_KEY=0x... pnpm smoke:local:evmThe legacy pnpm smoke:local alias still runs the EVM smoke test.
Solana:
X402_SOLANA_PAYER_PRIVATE_KEY='[...]' pnpm smoke:local:solanaWhat to pass:
X402_EVM_PAYER_PRIVATE_KEYis a normal EVM wallet private key: 32-byte hex, with or without0xX402_SOLANA_PAYER_PRIVATE_KEYis a base58 or JSON-encoded 64-byte Solana secret key- payer private keys are not server keys, Coinbase facilitator keys, or API keys
- for Solana, the payer key is not the facilitator
feePayer; the server discovers or configures that separately
If you use MetaMask:
- use the private key for the account that should pay for the x402 request
- in MetaMask, export the account private key and pass that exported value as
X402_EVM_PAYER_PRIVATE_KEY - the account should have enough funds on the chain required by the server challenge
- treat this key as highly sensitive and only use it in a secure local/dev environment you control
Defaults:
OPTIMAI_X402_BASE_URL=https://api-onchain.optimai.network- mainnet RPC for
eip155:8453defaults tohttps://mainnet.base.org - Solana RPC defaults to the public Solana mainnet/devnet endpoints
Optional overrides:
OPTIMAI_X402_QUERY="What is liquidation?"OPTIMAI_X402_BASE_URL=http://localhost:3002X402_EVM_BASE_RPC_URL=https://mainnet.base.orgX402_EVM_BASE_SEPOLIA_RPC_URL=https://sepolia.base.orgX402_SOLANA_MAINNET_RPC_URL=https://api.mainnet-beta.solana.comX402_SOLANA_DEVNET_RPC_URL=https://api.devnet.solana.comOPTIMAI_X402_RUN_CANCEL=1
Backward-compatible EVM aliases are also accepted:
X402_PAYER_PRIVATE_KEYX402_RPC_URL_BASEX402_RPC_URL_BASE_SEPOLIAX402_RPC_URL
Local compatibility test
For a stronger end-to-end check against api-server-onchain, use:
X402_EVM_PAYER_PRIVATE_KEY=0x... OPTIMAI_X402_BASE_URL=http://localhost:3002 pnpm compat:local:evmThe legacy pnpm compat:local alias still runs the EVM compatibility test.
This script validates the full x402 contract described in the server integration notes:
- create a paid search with query
Compare ETH and BTC - open
GET /external/v1/x402/search/:id/eventsusing the stored originalpayment-signature - wait for
search.completedandsearch.done - verify the completed SSE answer matches the final
GET /external/v1/x402/search/:idanswer - create a second search and verify
DELETE /external/v1/x402/search/:id - verify the cancelled search later returns:
status: "cancelled"x402_payment_status: "voided"
If the script finishes with:
[result]
Compatibility test completed successfully.then the SDK and the current api-server-onchain implementation are compatible for the tested create, SSE, GET, and DELETE flows.
Manual release checklist
Release is intentionally manual. From the package root:
pnpm install
pnpm build
pnpm test
X402_EVM_PAYER_PRIVATE_KEY=0x... OPTIMAI_X402_BASE_URL=http://localhost:3002 pnpm compat:local:evm
X402_SOLANA_PAYER_PRIVATE_KEY='[...]' pnpm smoke:local:solana
pnpm packWhat to verify before publish:
pnpm buildsucceedspnpm testsucceedspnpm compat:local:evmsucceeds against a runningapi-server-onchainpnpm smoke:local:solanasucceeds when validating Solana support with a funded Solana payerpnpm packproduces the tarball without missing entrypoints or type declarations
Publish steps:
npm whoami
npm publish --access publicPost-publish verification:
mkdir -p /tmp/optimai-x402-sdk-smoke
cd /tmp/optimai-x402-sdk-smoke
pnpm init
pnpm add @optimai-network/x402-sdkThen import createOptimaiX402Client and createViemPaymentHandler from the installed package and run a smoke request against api-server-onchain.
