@airwallet-ai/airwallet-sdk
v0.1.0
Published
SDK for publisher websites to interact with the AirWallet extension
Readme
AirWallet SDK (@airwallet-ai/airwallet-sdk)
Last Updated: 2025-11-02 (ERC-8004 trust + agent endpoints)
QA & Release Checklist
Run repository-level gates to exercise SDK journeys (
@developer,@extension,@payments-analyticssuites):pnpm quality:gatesPublish observability bundles (captures Playwright traces, API contracts, perf snapshots):
pnpm observability:publish
Overview
The AirWallet SDK now ships with a modular architecture centered around a configurable createAirWalletClient() factory and a legacy-compatible AirWalletSDK class. The new client separates capability detection, transport selection, UI callbacks, and telemetry so you can embed AirWallet flows in browsers, hybrid apps, or custom runtimes with finer control.
Note:
createAirWalletClient()+createPaidRouteHandler()(Next.js) are the current recommended integration path. The legacyAirWalletSDKwrapper exists for backwards compatibility only.
Features
- Detect AirWallet extension capabilities (EIP-1193 provider, postMessage transport).
- Configure UI callbacks for extension-missing, pending, and completion states.
- Select the optimal transport (provider or messaging) automatically, with graceful fallbacks.
- Fetch programmatic payments with typed Coinbase X402 helpers (
fetchWithPayment, selector overrides, mock requirements) on Base/USDC. - Discover monetized X402 resources with built-in pagination and network filters.
- Query ERC-8004 registry data via
createAgentClient()(trust scores, claims, attestations). - Publish and manage creator endpoints via
createCreatorClient()(dynamic pricing + trust metadata). - Emit unlock events and telemetry signals for observability.
Installation
Install the package using your preferred package manager:
# Using pnpm (recommended for this monorepo)
pnpm add @airwallet-ai/airwallet-sdk
# Using npm
npm install @airwallet-ai/airwallet-sdk
# Using yarn
yarn add @airwallet-ai/airwallet-sdkConfiguration & Initialization
Option 1: New client factory (recommended)
import {
createAirWalletClient,
type SdkConfig,
type UnlockRequest,
} from "@airwallet-ai/airwallet-sdk";
const client = createAirWalletClient({
publisher: { id: "YOUR_PUBLISHER_ID" },
environment: {
apiBaseUrl: "https://api.airwallet.ai",
},
ui: {
onExtensionMissing: ({ publisher }) => {
console.warn("Extension missing for", publisher.id);
},
onPaymentPending: ({ contentId }) => {
console.log("Payment pending for", contentId);
},
onPaymentComplete: ({ contentId, transactionId }) => {
console.log("Payment complete", contentId, transactionId);
},
},
});
const request: UnlockRequest = {
contentId: "demo-article",
price: { amount: 0.25, currency: "USD" },
metadata: { title: "Premium Article" },
};
await client.unlock(request);Handling X402-protected APIs
The client now includes first-class helpers for Coinbase X402 flows. Configure the wallet signer and optional per-environment defaults:
import { createAirWalletClient } from "@airwallet-ai/airwallet-sdk";
import { privateKeyToAccount } from "viem/accounts";
const wallet = privateKeyToAccount("0xYOUR_SIGNER_PRIVATE_KEY");
const client = createAirWalletClient({
publisher: {
id: "YOUR_PUBLISHER_ID",
accessToken: process.env.AIRWALLET_PUBLISHER_TOKEN,
},
environment: {
apiBaseUrl: "http://127.0.0.1:5180",
x402: {
maxUsdCents: 10, // defaults to $0.10 in base units
defaultTimeoutMs: 15_000,
preferredNetworks: ["base"],
discoveryOrigin: "https://airwallet.ai",
},
},
x402: {
wallet,
// Optional: customize payment requirements selection
paymentRequirementsSelector: (requirements, networkHint) => {
return (
requirements.find((req) => req.network === networkHint) ??
requirements[0]
);
},
// Optional: provide pre-built requirements during tests
mockPaymentRequirements: [
{
scheme: "exact",
network: "base",
asset: "usdc",
maxAmountRequired: "0.001",
resource: "https://api.airwallet.ai/api/monetized/echo",
description: "Mock pay-per-request endpoint",
mimeType: "application/json",
payTo: "0x0000000000000000000000000000000000000000",
maxTimeoutSeconds: 30,
},
],
disableAutoPay: false,
strictErrors: true,
},
});
const response = await client.fetchWithPayment(
"http://127.0.0.1:5180/api/monetized/echo?q=demo",
);
const payload = client.decodePaymentResponse(
response.headers.get("x-payment-response") ?? "",
);
console.log(await response.json());
console.log("Payment receipt", payload);The helper automatically:
- Retries the request when a
402 Payment Requiredresponse is returned. - Verifies the payment amount is within the configured
maxUsdCentsthreshold (converted to Base USDC units). - Applies the configured timeout via an
AbortControllerto avoid hanging fetches. - Surfaces typed
AirWalletErrorcodes (X402_PAYMENT_REQUIRED,X402_PAYMENT_LIMIT_EXCEEDED,X402_PAYMENT_FAILED).
If no wallet client is supplied, fetchWithPayment will throw X402_PAYMENT_FAILED so you can render fallback UX.
Receipts: The decoded
X-PAYMENT-RESPONSEmirrors server-side entries used to power the user receipts/library. Store this response if you need local audit trails; the server already records a canonical copy.
Next.js Paid Route Handlers
For sellers creating paid API endpoints, the SDK provides createPaidRouteHandler() for Next.js App Router:
// app/api/paid/echo/route.ts
import { createPaidRouteHandler } from "@airwallet-ai/airwallet-sdk";
export const GET = createPaidRouteHandler(
{
price: "$0.001",
description: "Echo service - pay per request",
onEvent: (event) => console.log(`[x402] ${event.type}`),
},
async (req) => {
const url = new URL(req.url);
return Response.json({ echo: url.searchParams.get("q") ?? "ok" });
},
);Network defaults:
- In development, the paid route defaults to
base-sepolia. - In production, it defaults to
base. - Override with
X402_NETWORK=base(orbase-sepolia) or passnetworkexplicitly.
Options:
price: Price in"$0.01","0.01 USD", or numeric formatnetwork:"base"or"base-sepolia"description: Shown in 402 challengeonEvent: Callback for telemetry events
Bypass for development:
curl -H "x-airwallet-bypass: true" http://localhost:3000/api/paid/echoSee apps/template-paid-api for a complete example.
Discovery helper
You can fetch available X402-protected resources by origin, network, and kind:
const resources = await client.discoverResources({
origin: "https://api.airwallet.ai",
kinds: ["http"],
network: "base",
limit: 25,
});
console.table(
resources.map((item) => ({
resource: item.resource,
network: item.accepts[0]?.network,
amount: item.accepts[0]?.maxAmountRequired,
})),
);When environment.x402.discoveryOrigin is provided, it becomes the default for subsequent calls, and preferredNetworks are forwarded to the selector. Discovery is sourced from Coinbase's x402 Bazaar and curated by AirWallet.
Agent registry client (ERC-8004)
import { createAgentClient } from "@airwallet-ai/airwallet-sdk";
const agents = createAgentClient({
environment: { apiBaseUrl: "http://127.0.0.1:5180" },
});
const list = await agents.listAgents({ query: "search-term", limit: 20 });
const detail = await agents.getAgentDetails(list[0].id);
await agents.submitAttestation(detail.agent.id, {
outcome: "SUCCESS",
confidence: 0.9,
metadata: { latencyMs: 350 },
});- Each agent response includes trust scores (latest window), verification markers, and published endpoints.
- Attestation payloads map directly to
/api/agents/:id/attestand are signed server-side when required.
Creator endpoints client
import { createCreatorClient } from "@airwallet-ai/airwallet-sdk";
const creators = createCreatorClient({
environment: { apiBaseUrl: "http://127.0.0.1:5180" },
auth: { token: "creator-session-token" },
});
const endpoints = await creators.listEndpoints("creator-id", { limit: 10 });
const created = await creators.createEndpoint("creator-id", {
contentId: "demo-content",
url: "https://api.example.com/agents/demo",
description: "Premium agent endpoint",
pricingPolicyId: "policy-id",
});
await creators.updateEndpoint("creator-id", created.id, {
description: "Updated description",
status: "ACTIVE",
});- The client manages pricing policy wiring, trust metadata, and SLO overrides.
- Requires authenticated creator token; fetch via the user portal or the new
/api/authflows.
Telemetry & Correlation
You can enable lightweight tracing and consistent correlation IDs across SDK operations (manifest, ledger, attestation, unlock fallback):
import { createAirWalletClient } from "@airwallet-ai/airwallet-sdk";
const tracer = {
startSpan: (name: string, attributes?: Record<string, unknown>) => {
// connect to your observability tool here
return {
end: () => {},
setAttribute: () => {},
recordException: () => {},
};
},
};
const client = createAirWalletClient({
publisher: { id: "YOUR_PUBLISHER_ID", accessToken: "<token>" },
environment: { apiBaseUrl: "http://127.0.0.1:5180" },
telemetry: {
tracer,
correlationIdFactory: () => `site-${Date.now()}`,
},
});
// SDK will attach X-Correlation-Id and X-Trace-Id per request
await client.getPolicyManifest();
await client.getLedgerEntries();
await client.attestPolicyOutcome({
outcome: "approved",
manifest: {
version: 1,
signature: "sig",
issuedAt: "now",
expiresAt: "later",
},
});Capability Ping
Detect the preferred transport quickly, without initiating an unlock flow:
const status = await client.ping();
// status.transport is one of: "provider" | "message" | "none"
// status.providerDetected, status.hasMessaging booleans availableRetries & Backoff
The client will retry transient HTTP failures (5xx/429) for manifest and ledger requests using exponential backoff. Configure via SdkConfig.retry:
const client = createAirWalletClient({
publisher: { id: "YOUR_PUBLISHER_ID", accessToken: "<token>" },
environment: { apiBaseUrl: "https://api.airwallet.ai" },
retry: {
requestMaxAttempts: 3, // default 3
requestBackoffMs: 200, // default 200ms per attempt (exponential)
},
});Fallback behavior (BYO wallet / no extension)
When neither transport is available, createAirWalletClient() triggers the ui.onExtensionMissing callback, emits an extension_missing event, and (for the legacy wrapper) renders a modal prompting users to install the extension. Both the client and legacy wrapper throw an AirWalletError with code EXTENSION_NOT_FOUND so you can render custom UX.
Migration guide
- Replace direct
AirWalletSDKusage withcreateAirWalletClient()to gain access to events, telemetry, and custom UI callbacks. - Configure your signer/provider to use Base USDC (e.g., viem wallet client or injected EIP-1193 provider). The AirWallet browser extension is optional; any EIP‑1193 wallet is supported.
- Update unlock calls to pass the new
UnlockRequestshape (price: { amount, currency }). - Handle the new unlock events (
extension_missing,payment_pending,payment_complete) emitted via the returned client. - Remove manual
window.postMessagemocks; the SDK now encapsulates transport selection for you.
Development
Testing
Run the Vitest suite for this package:
pnpm --filter @airwallet-ai/airwallet-sdk testTests cover the legacy wrapper, capability detection, and transport fallbacks. Add new cases under packages/airwallet-sdk/tests/ following the existing patterns.
Building
The package is bundled via tsup (ESM/CJS/IIFE) with types emitted by the compiler. Run pnpm build to generate updated artifacts before publishing.
Security Notes
- Your
publisherIdis used for identification with AirWallet systems. - The SDK communicates with the AirWallet browser extension via
window.postMessage.
License
This package is part of the AirWallet project and is subject to the project's proprietary license. See the LICENSE file in the root directory for details.
Wallet Strategy & Providers
AirWallet is moving to an open X402-first model where users can bring their own wallet. The SDK does not ship a custodial wallet. Instead, you should detect an existing EIP-1193 provider (MetaMask, Coinbase Wallet, Phantom, Rainbow, etc.) and pass a configured signer or provider into the client. The AirWallet browser extension remains optional and provides policy controls, spend limits, and history, but is not required for X402 payments.
Recommended defaults
- Network: Base (
base,base-sepoliafor testing) - Asset: USDC
- Preferred providers: any injected EIP-1193 wallet with
eth_signTypedData_v4support - SDK signer: supply a viem-compatible account or
walletClient
import { createWalletClient, custom } from "viem";
import { base } from "viem/chains";
const walletClient = createWalletClient({
chain: base,
transport: custom(window.ethereum!),
});
const client = createAirWalletClient({
publisher: { id: "demo-publisher" },
environment: {
apiBaseUrl: "https://api.airwallet.ai",
x402: {
preferredNetworks: ["base"],
discoveryOrigin: "https://airwallet.ai",
},
},
x402: {
wallet: walletClient,
disableAutoPay: true,
},
ui: {
onExtensionMissing: () => {
console.warn(
"No AirWallet extension detected. Continuing with BYO wallet.",
);
},
},
});If you need to support both the AirWallet extension and third-party wallets, detect the extension via client.ping() and fall back to window.ethereum when the extension is not connected.
