EarnKit SDK Reference
Complete API documentation for the EarnKit TypeScript SDK. This SDK enables developers to add usage-based monetization to AI agents with reliable billing, wallet integration, and flexible fee models.
Architecture Overview
Working and Flow
This is the flow of the EarnKit SDK with an example gemini based chat application.
Configuration
EarnKitConfig
Configuration object for initializing the SDK.
interface EarnKitConfig {
agentId: string; // Required: Your agent ID from the dashboard
baseUrl?: string; // Optional: API base URL (default: http://localhost:3000)
debug?: boolean; // Optional: Enable debug logging (default: false)
requestTimeoutMs?: number; // Optional: Request timeout (default: 30000ms)
}
Example:
const earnkit = new EarnKit({
agentId: 'clxxxxx', // CUID from your dashboard
baseUrl: 'https://api.yourapp.com',
debug: process.env.NODE_ENV === 'development',
requestTimeoutMs: 60000 // 60 seconds
});
Core Methods
track()
Initiates a billable event with provisional charging. This method checks for sufficient funds/credits and creates a pending usage event.
track(params: TrackParams): Promise<{ eventId: string }>
Parameters:
interface TrackParams {
walletAddress: string; // Required: User's wallet address (0x...)
idempotencyKey?: string; // Optional: UUID for duplicate prevention
creditsToDeduct?: number; // Optional: Credits to deduct (credit-based models)
}
Returns:
Promise<{ eventId: string }>
- Unique event ID for capture/release
Examples:
Free Tier Model:
// Charges 0.0001 ETH after 50 free prompts
const { eventId } = await earnkit.track({
walletAddress: '0x742d35Cc6435C0532925a3b8b98C31d5b1c9c2c4'
});
Credit-Based Model:
// Deducts specified credits from user balance
const { eventId } = await earnkit.track({
walletAddress: '0x742d35Cc6435C0532925a3b8b98C31d5b1c9c2c4',
creditsToDeduct: 25 // Override default credits per prompt
});
With Idempotency:
import { v4 as uuidv4 } from 'uuid';
const { eventId } = await earnkit.track({
walletAddress: '0x742d35Cc6435C0532925a3b8b98C31d5b1c9c2c4',
idempotencyKey: uuidv4() // Prevents duplicate charges
});
Throws:
EarnKitInputError
- Invalid wallet address or parametersEarnKitApiError
- Insufficient funds/credits (status 402) or API errors
capture()
Finalizes a pending usage event, confirming the charge. Call this after successful AI operation completion.
capture(params: CaptureParams): Promise<{ success: boolean }>
Parameters:
interface CaptureParams {
eventId: string; // Required: Event ID from track() call
}
Example:
const { eventId } = await earnkit.track({ walletAddress });
try {
const aiResponse = await generateAIResponse(prompt);
// Confirm the charge
await earnkit.capture({ eventId });
return aiResponse;
} catch (error) {
await earnkit.release({ eventId });
throw error;
}
Throws:
EarnKitInputError
- Invalid event IDEarnKitApiError
- Event not found or not capturable (status 404)
release()
Releases a pending usage event, refunding any provisional charges. Call this when AI operations fail.
release(params: ReleaseParams): Promise<{ success: boolean }>
Parameters:
interface ReleaseParams {
eventId: string; // Required: Event ID from track() call
}
Example:
const { eventId } = await earnkit.track({ walletAddress });
try {
const result = await riskyAIOperation();
await earnkit.capture({ eventId });
return result;
} catch (error) {
// Refund user on failure
await earnkit.release({ eventId });
console.error('AI operation failed, charges refunded');
throw error;
}
Throws:
EarnKitInputError
- Invalid event IDEarnKitApiError
- Event not found or not releasable
Balance Management
getBalance()
Retrieves the current ETH and credit balances for a user’s wallet.
getBalance(params: { walletAddress: string }): Promise<UserBalance>
Parameters:
interface { walletAddress: string } // User's wallet address
Returns:
interface UserBalance {
eth: string; // ETH balance as string (e.g., "0.005")
credits: string; // Credit balance as string (e.g., "150")
}
Example:
const balance = await earnkit.getBalance({
walletAddress: '0x742d35Cc6435C0532925a3b8b98C31d5b1c9c2c4'
});
console.log(`User has ${balance.eth} ETH and ${balance.credits} credits`);
// Check if user has sufficient credits before operation
const requiredCredits = 10n; // Use BigInt for safety
if (BigInt(balance.credits) < requiredCredits) {
throw new Error('Insufficient credits. Please top up.');
}
Top-up & Credit Management
getTopUpDetails()
Fetches available top-up options configured for the agent.
getTopUpDetails(): Promise<TopUpDetailsResponse>
Returns:
interface TopUpDetailsResponse {
options: TopUpOption[];
}
interface TopUpOption {
label: string; // Display label (e.g., "100 Credits")
amountInEth: string; // ETH amount (e.g., "0.001")
to: string; // Developer's deposit address
value: string; // Amount in Wei for transactions
creditsToTopUp?: number; // Credits to add (credit-based models)
}
Example:
const { options } = await earnkit.getTopUpDetails();
options.forEach(option => {
console.log(`${option.label}: ${option.amountInEth} ETH`);
// Example output: "100 Credits: 0.001 ETH"
});
// Use with wallet for transaction
const selectedOption = options[0];
const tx = await wallet.sendTransaction({
to: selectedOption.to,
value: BigInt(selectedOption.value) // Use BigInt to avoid precision loss
});
submitTopUpTransaction()
Submits a completed transaction hash for monitoring and credit processing.
submitTopUpTransaction(params: SubmitTopUpParams): Promise<SubmitTopUpResponse>
Parameters:
interface SubmitTopUpParams {
txHash: string; // Transaction hash from blockchain
walletAddress: string; // User's wallet address
amountInEth: string; // ETH amount sent
creditsToTopUp?: number; // Credits to add (credit-based models)
}
Returns:
interface SubmitTopUpResponse {
status: string; // "PENDING_CONFIRMATION"
message: string; // Status message
}
Example:
// After user's wallet sends the transaction
const txResponse = await wallet.sendTransaction({
to: option.to,
value: BigInt(option.value)
});
// Submit the hash for monitoring
const response = await earnkit.submitTopUpTransaction({
txHash: txResponse.hash,
walletAddress: userAddress,
amountInEth: option.amountInEth,
creditsToTopUp: option.creditsToTopUp
});
console.log(response.message); // "Top-up transaction is being monitored."
pollForBalanceUpdate()
Utility method to poll for balance changes after submitting a top-up transaction.
pollForBalanceUpdate(params: PollingParams): void
Parameters:
interface PollingParams {
walletAddress: string;
initialBalance: UserBalance;
onConfirmation: (newBalance: UserBalance) => void;
onTimeout?: () => void;
pollInterval?: number; // Default: 10000ms (10 seconds)
maxPolls?: number; // Default: 30 (5 minutes total)
}
Example:
// Get initial balance
const initialBalance = await earnkit.getBalance({ walletAddress });
// Submit top-up transaction (code from previous example)
await earnkit.submitTopUpTransaction({
txHash,
walletAddress,
amountInEth: "0.001",
creditsToTopUp: 100
});
// Poll for updates
earnkit.pollForBalanceUpdate({
walletAddress,
initialBalance,
onConfirmation: (newBalance) => {
console.log('Balance updated!', newBalance);
// Update UI with new balance
updateUserInterface(newBalance);
},
onTimeout: () => {
console.log('Transaction confirmation timed out');
// Handle timeout (transaction may still be processing)
},
pollInterval: 5000, // Check every 5 seconds
maxPolls: 60 // Timeout after 5 minutes
});
Error Handling
The SDK provides specific error types for different failure scenarios:
Error Types
import {
EarnKitInitializationError,
EarnKitInputError,
EarnKitApiError
} from 'earnkit-sdk';
EarnKitInitializationError
Thrown during SDK initialization with invalid configuration.
try {
const earnkit = new EarnKit({ agentId: '' }); // Invalid empty agentId
} catch (error) {
if (error instanceof EarnKitInitializationError) {
console.error('SDK initialization failed:', error.message);
}
}
EarnKitInputError
Thrown when method parameters are invalid.
try {
await earnkit.track({ walletAddress: 'invalid-address' });
} catch (error) {
if (error instanceof EarnKitInputError) {
console.error('Invalid input:', error.message);
// Handle validation error
}
}
EarnKitApiError
Thrown when API calls fail. Contains HTTP status and response details.
try {
await earnkit.track({ walletAddress });
} catch (error) {
if (error instanceof EarnKitApiError) {
console.error(`API Error ${error.status}:`, error.message);
switch (error.status) {
case 402:
console.log('Insufficient funds - prompt user to top up');
break;
case 404:
console.log('Agent not found - check configuration');
break;
case 500:
console.log('Server error - retry later');
break;
}
}
}
Comprehensive Error Handling Example
async function processAIRequest(prompt: string, walletAddress: string) {
let eventId: string | null = null;
try {
// Track usage
const result = await earnkit.track({ walletAddress });
eventId = result.eventId;
// Process AI request
const aiResponse = await generateAIResponse(prompt);
// Confirm charge
await earnkit.capture({ eventId });
return aiResponse;
} catch (error) {
// Release pending charge on any failure
if (eventId) {
try {
await earnkit.release({ eventId });
} catch (releaseError) {
console.error('Failed to release charge:', releaseError);
}
}
// Handle specific error types
if (error instanceof EarnKitApiError) {
switch (error.status) {
case 402:
throw new Error('Insufficient funds. Please top up your balance.');
case 404:
throw new Error('Service configuration error. Please contact support.');
default:
throw new Error('Service temporarily unavailable. Please try again.');
}
}
if (error instanceof EarnKitInputError) {
throw new Error('Invalid request. Please check your input.');
}
// Re-throw other errors
throw error;
}
}
Best Practices
1. Always Use Capture/Release Pattern
// ✅ Good: Proper error handling with release
const { eventId } = await earnkit.track({ walletAddress });
try {
const result = await aiOperation();
await earnkit.capture({ eventId });
return result;
} catch (error) {
await earnkit.release({ eventId }); // Always release on failure
throw error;
}
// ❌ Bad: Missing release on failure
const { eventId } = await earnkit.track({ walletAddress });
const result = await aiOperation(); // If this fails, user is charged but gets no result
await earnkit.capture({ eventId });
2. Use Idempotency for Critical Operations
import { v4 as uuidv4 } from 'uuid';
// Generate idempotency key per unique operation
const operationId = uuidv4();
const { eventId } = await earnkit.track({
walletAddress,
idempotencyKey: operationId // Prevents duplicate charges on retry
});
3. Check Balances Before Expensive Operations
async function expensiveAIOperation(walletAddress: string) {
// Check balance first for better UX
const balance = await earnkit.getBalance({ walletAddress });
if (BigInt(balance.credits) < 50n) { // Use BigInt for safety
throw new Error('Insufficient credits for this operation');
}
// Proceed with tracking
const { eventId } = await earnkit.track({
walletAddress,
creditsToDeduct: 50
});
// ... rest of operation
}
4. Implement Proper Logging
const earnkit = new EarnKit({
agentId: process.env.EARNKIT_AGENT_ID!,
debug: process.env.NODE_ENV === 'development'
});
// The SDK will log detailed information when debug: true
5. Handle Network Errors Gracefully
try {
await earnkit.track({ walletAddress });
} catch (error) {
if (error instanceof EarnKitApiError && error.status === 0) {
// Network error - the SDK has built-in retry logic
console.log('Network error occurred, SDK will retry automatically');
}
}
Rate Limiting & Retry Logic
The SDK includes built-in retry logic for reliability:
- Automatic Retries: Up to 2 retries (1 initial + 2 retries) for server errors (5xx) and timeouts
- Exponential Backoff: 1s, 2s, 4s delays between retries
- Request Timeout: 30 seconds default (configurable)
- Error Classification: Only retries transient errors (server errors and timeouts)
// SDK automatically retries transient failures
const earnkit = new EarnKit({
agentId: 'your-agent-id',
requestTimeoutMs: 60000 // Increase timeout for slow networks
});
TypeScript Support
The SDK is fully typed with TypeScript. Import types as needed:
import {
// Core Class
EarnKit,
// Configuration & Data Types
EarnKitConfig,
UserBalance,
TopUpOption,
// Custom Errors
EarnKitApiError,
EarnKitInputError,
EarnKitInitializationError
} from 'earnkit-sdk';
// All methods are properly typed
const balance: UserBalance = await earnkit.getBalance({ walletAddress });
const config: EarnKitConfig = { agentId: 'test' };