X402 Payment Protocol
AiMo Network uses the X402 protocol for all payment processing. X402 enables direct cryptocurrency payments for AI model inference with automatic payment verification.
Overview
X402 payments are used in two ways on AiMo Network:
- Managed (with API Key): Automatic X402 flow using Coinbase server wallets - See API Key Method
- Direct (without API Key): Manual X402 implementation by client - See Direct X402 Method
Using X402 Direct Payment
When calling the API without an Authorization header, you implement the X402 protocol yourself.
Endpoint Details
Base URL: https://devnet.aimo.network/api
Endpoint: POST /v1/chat/completions
- Solana Mainnet: USDC payments
- Base Mainnet: USDC payments
Quick Start with X402 SDK
Installation
npm install x402-fetch viemBasic Usage with Base
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { wrapFetchWithPayment } from "x402-fetch";
import { base } from "viem/chains";
// Create a wallet client
const account = privateKeyToAccount("0xYourPrivateKey");
const client = createWalletClient({
account,
transport: http(),
chain: base,
});
// Wrap the fetch function with payment handling
const fetchWithPay = wrapFetchWithPayment(fetch, client);
// Make a request - payment is handled automatically
const response = await fetchWithPay("https://devnet.aimo.network/api/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "openai/gpt-4o",
messages: [
{ role: "user", content: "Explain quantum computing" }
],
max_tokens: 500
})
});
const data = await response.json();
console.log(data);The x402-fetch SDK automatically:
- Detects 402 Payment Required responses
- Parses payment requirements
- Creates and signs the payment transaction
- Retries the request with payment proof
- Returns the final response
Streaming Responses
The X402 SDK works seamlessly with streaming responses:
const response = await fetchWithPay("https://devnet.aimo.network/api/v1/chat/completions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "anthropic/claude-3-5-sonnet",
messages: [{ role: "user", content: "Tell me a story" }],
stream: true,
max_tokens: 500
})
});
// Process streaming response
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
process.stdout.write(chunk);
}Advanced Configuration
Customize the SDK behavior with options:
const fetchWithPay = wrapFetchWithPayment(fetch, client, {
onPaymentRequired: (paymentInfo) => {
console.log('Payment required:', paymentInfo);
},
onPaymentSent: (txHash) => {
console.log('Payment sent:', txHash);
},
onError: (error) => {
console.error('Payment error:', error);
},
paymentTimeout: 120000, // 2 minutes in milliseconds
});Request Format
The endpoint accepts standard OpenAI-compatible request format:
{
"model": "openai/gpt-4o",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Your question here"}
],
"stream": false,
"max_tokens": 500,
"temperature": 0.7,
"top_p": 1.0
}model(required): Model name (e.g.,openai/gpt-4o) or direct format (provider_pubkey:model_name)messages(required): Array of message objects withroleandcontentstream: Enable streaming responses (default: false)max_tokens: Maximum tokens to generatetemperature: Sampling temperature (0.0 to 2.0)top_p: Nucleus sampling parameter- All other standard OpenAI parameters
Network Selection
The endpoint returns payment options for both Solana and Base networks. You can choose which network to use for payment:
Via Header (Optional):curl -X POST "https://devnet.aimo.network/api/v1/chat/completions" \
-H "X-Preferred-Network: solana" \
-H "Content-Type: application/json" \
-d '...'Supported values: solana, base
Error Handling
Payment Expired
If you don't complete payment within the time limit:
{
"error": {
"message": "Payment request expired",
"type": "payment_expired",
"code": 402
}
}Solution: Start a new payment request.
Payment Verification Failed
If the payment transaction cannot be verified:
{
"error": {
"message": "Payment verification failed",
"type": "payment_verification_failed",
"code": 402
}
}- Transaction not yet confirmed on-chain
- Incorrect payment amount
- Payment sent to wrong address
- Transaction signature invalid
Solution: Wait for transaction confirmation and retry, or verify payment details.
Insufficient Payment
If the payment amount is less than required:
{
"error": {
"message": "Insufficient payment amount",
"type": "insufficient_payment",
"code": 402,
"details": {
"required": "0.05",
"received": "0.03"
}
}
}Solution: Submit the correct payment amount.
Best Practices
Transaction Confirmation
Always wait for transaction confirmation before submitting payment proof:
- Solana: Wait for "confirmed" or "finalized" commitment level
- Base: Wait for at least 1 block confirmation
Payment Expiration
Payment requests expire based on the maxTimeoutSeconds field (typically 300 seconds / 5 minutes). Plan your payment flow accordingly:
- Read the
maxTimeoutSecondsvalue from the payment option - Calculate expiration time and show a countdown to users
- Request a new payment if expired
const solanaOption = paymentResponse.accepts.find(opt => opt.network === 'solana');
const expiresAt = Date.now() + (solanaOption.maxTimeoutSeconds * 1000);
console.log(`Payment must be completed by: ${new Date(expiresAt)}`);Gas Fees
Factor in blockchain gas fees when displaying total costs to users:
- Solana: Typically ~0.000005 SOL per transaction
- Base: Variable based on network congestion
Error Recovery
The X402 SDK handles errors automatically, but you can add custom error handling:
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { wrapFetchWithPayment } from "x402-fetch";
import { base } from "viem/chains";
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const client = createWalletClient({
account,
transport: http(),
chain: base,
});
const fetchWithPay = wrapFetchWithPayment(fetch, client, {
onError: async (error, retryCount) => {
console.error(`Payment error (attempt ${retryCount}):`, error);
if (retryCount < 3) {
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
return true; // Continue retrying
}
return false; // Stop retrying
}
});
try {
const response = await fetchWithPay("https://devnet.aimo.network/api/v1/chat/completions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "deepseek/deepseek-chat",
messages: [{ role: "user", content: "Hello!" }],
max_tokens: 100
})
});
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Request failed after retries:", error);
}Manual X402 Flow (Advanced)
For advanced users who need direct control, you can implement the X402 flow manually without the SDK.
Manual Request Flow
- Make initial request without payment
- Receive 402 response with payment options
- Create and submit payment transaction on your chosen network
- Retry request with
X-Payment: {network}:{transaction_signature}header
{
"error": "X-PAYMENT header is required",
"accepts": [
{
"network": "solana",
"maxAmountRequired": "123",
"payTo": "Ge3jkza5KRfXvaq3GELNLh6V1pjjdEKNpEdGXJgjjKUR",
"maxTimeoutSeconds": 300,
"asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
},
{
"network": "base",
"maxAmountRequired": "123",
"payTo": "0xf59bcCde20a59d49Ab0384BEB8fd874D28C46dEB",
"maxTimeoutSeconds": 300,
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
}
],
"x402Version": 1
}curl -X POST "https://devnet.aimo.network/api/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "X-Payment: base:0x1a2b...transaction-hash...3c4d" \
-d '{...your request...}'For full manual implementation details, refer to the X402 Protocol Specification.
Comparison of Payment Methods
| Feature | API Key (Managed X402) | Direct X402 |
|---|---|---|
| Authentication | API key required | None required |
| Account Setup | Dashboard account | Wallet only |
| Payment Method | Automatic (Coinbase wallets) | Manual blockchain tx |
| Payment Flow | Transparent | Explicit 402 flow |
| Transaction Fees | Minimal (managed) | Gas fees per request |
| SDK Support | Standard HTTP client | x402-fetch required |
| Best For | Production apps, high volume | Decentralized apps, testing |
Security Considerations
Payment Verification
All payments are verified on-chain before processing requests. The system:
- Checks transaction exists and is confirmed
- Verifies recipient address matches
- Confirms payment amount is sufficient
- Ensures payment_id hasn't been used before
Private Key Safety
Never expose your private keys in your code:
// ❌ DON'T: Hardcode private keys
const account = privateKeyToAccount("0x1234567890abcdef...");
// ✅ DO: Use environment variables
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
// ✅ DO: Use secure key management in production
// For browser apps, use wallet connection libraries:
// - Base/EVM: wagmi, ConnectKit, RainbowKit
// - Solana: @solana/wallet-adapterRate Limiting
The X402 endpoint has rate limits to prevent abuse:
- Maximum 10 pending payment requests per IP
- Maximum 100 requests per hour per IP
SDK Resources
- X402 Fetch: Official JavaScript/TypeScript SDK
- X402 Protocol: Official Documentation
- Examples: Check the x402-fetch repository for complete usage examples
Support
For issues with X402 payments:
- Check the x402-fetch documentation for SDK-specific help
- Review Error Handling documentation
- Verify transaction on blockchain explorer:
- Solana: Solscan or Solana Explorer
- Base: Basescan
- Join our Discord server for community support
- Contact support@aimo.network for direct assistance
See Also
- Standard API Usage - Using the API with escrow accounts
- Payment and Billing - General payment information
- Authentication - API key setup for standard endpoint
- Streaming - Using streaming responses with X402