Overview
The /track endpoint records usage events for your metered features. Use it to:
Track API calls, messages, or other usage-based features
Deduct from customer balances in real-time
Bill customers for overage usage
Log events with custom properties for analytics
Quick Start
React Frontend
Backend (Node.js)
Python
import { useCustomer } from '@useautumn/autumn-js/react' ;
function MessageComposer () {
const customer = useCustomer ();
const sendMessage = async () => {
// Send the message
await sendMessageAPI ( messageContent );
// Track usage (fires in background)
await fetch ( '/api/autumn/track' , {
method: 'POST' ,
body: JSON . stringify ({
featureId: "messages" ,
value: 1
})
});
};
return < button onClick = { sendMessage } > Send Message </ button > ;
}
Parameters
The ID of the customer whose usage to track.
The ID of the feature to track. Use either featureId or eventName.
Event name to track usage for. Use instead of featureId when a single event should update multiple features. // Track one event that updates multiple features
track ({
customerId: "cus_123" ,
eventName: "api_request" , // Updates both "api_calls" and "bandwidth"
value: 1 ,
properties: { bytes: 2048 }
})
The ID of the entity for entity-scoped tracking (e.g., per-workspace usage).
The amount of usage to record. Use negative values to credit balance (e.g., when removing a seat). // Deduct 500 tokens
track ({ featureId: "ai_tokens" , value: 500 })
// Credit back 100 tokens (e.g., refund)
track ({ featureId: "ai_tokens" , value: - 100 })
Additional properties to attach to the usage event for analytics and debugging. track ({
featureId: "api_calls" ,
value: 1 ,
properties: {
endpoint: "/v1/generate" ,
model: "gpt-4" ,
responseTime: 1250 ,
userId: "usr_xyz"
}
})
Response
interface TrackResponse {
customerId : string ; // The customer ID
entityId ?: string ; // The entity ID (if entity-scoped)
eventName ?: string ; // Event name (if tracking by event)
value : number ; // Amount recorded
balance : Balance | null ; // Updated balance (null for event-based)
balances ?: Record < string , Balance >; // Multiple balances (for event-based)
}
interface Balance {
featureId : string ;
granted : number ; // Total balance granted
remaining : number ; // Balance remaining after this usage
usage : number ; // Total usage in current period
unlimited : boolean ; // Whether feature is unlimited
overageAllowed : boolean ; // Whether overage is allowed
nextResetAt : number | null ; // Timestamp of next reset
}
Common Use Cases
Track API Calls
app . post ( '/api/generate' , async ( req , res ) => {
// Process the API request
const result = await generateContent ( req . body );
// Track the usage
await autumn . balances . track ({
customerId: req . user . id ,
featureId: "api_calls" ,
value: 1 ,
properties: {
endpoint: "/api/generate" ,
model: req . body . model ,
tokensUsed: result . tokens
}
});
res . json ( result );
});
Track Variable Usage (Tokens, Storage, etc.)
// Track AI token usage
const completion = await openai . completions . create ({
model: "gpt-4" ,
prompt: userPrompt
});
await autumn . balances . track ({
customerId: "cus_123" ,
featureId: "ai_tokens" ,
value: completion . usage . total_tokens , // Variable amount
properties: {
model: "gpt-4" ,
promptTokens: completion . usage . prompt_tokens ,
completionTokens: completion . usage . completion_tokens
}
});
Event-Based Tracking (One Event, Multiple Features)
Use eventName to track a single event that updates multiple feature balances:
// One event updates both "api_calls" and "ai_tokens" features
const result = await autumn . balances . track ({
customerId: "cus_123" ,
eventName: "ai_completion" , // Event name instead of featureId
value: 1 ,
properties: {
model: "gpt-4" ,
tokensUsed: 1500
}
});
// Response includes balances for all affected features
console . log ( result . balances );
// {
// "api_calls": { remaining: 99, ... },
// "ai_tokens": { remaining: 8500, ... }
// }
Credit Usage (Negative Values)
Use negative values to add back to a customer’s balance:
// Customer removed a team member, credit back the seat
await removeTeamMember ( memberId );
await autumn . balances . track ({
customerId: "cus_123" ,
featureId: "seats" ,
value: - 1 , // Credit back 1 seat
properties: {
action: "member_removed" ,
memberId: memberId
}
});
Entity-Scoped Tracking (Per-Workspace)
// Track usage for a specific workspace
await autumn . balances . track ({
customerId: "cus_123" ,
entityId: "workspace_abc" , // Track for specific workspace
featureId: "api_calls" ,
value: 1 ,
properties: {
workspaceName: "Production"
}
});
Idempotency
For critical operations, use an idempotency key to prevent duplicate tracking:
import { v4 as uuidv4 } from 'uuid' ;
const requestId = uuidv4 ();
// This request will only be processed once, even if retried
await autumn . balances . track ({
customerId: "cus_123" ,
featureId: "credits" ,
value: 100 ,
idempotencyKey: requestId // Internal parameter
});
Idempotency keys are automatically handled for you in most cases. Only use manual idempotency keys for critical operations where duplicate tracking would cause issues.
Error Handling
try {
await autumn . balances . track ({
customerId: "cus_123" ,
featureId: "api_calls" ,
value: 1
});
} catch ( error ) {
if ( error . code === "insufficient_balance" ) {
// Customer exceeded their limit and overage not allowed
return res . status ( 403 ). json ({
error: "Usage limit exceeded"
});
} else if ( error . code === "feature_not_found" ) {
// Feature doesn't exist
console . error ( "Invalid feature ID" );
}
}
Check Before Track
To prevent exceeding limits, check access before tracking:
// Option 1: Check then track separately
const checkResult = await autumn . balances . check ({
customerId: "cus_123" ,
featureId: "ai_tokens" ,
requiredBalance: 1000
});
if ( ! checkResult . allowed ) {
return res . status ( 403 ). json ({ error: "Insufficient tokens" });
}
await processAIRequest ();
await autumn . balances . track ({
customerId: "cus_123" ,
featureId: "ai_tokens" ,
value: 1000
});
// Option 2: Atomic check + track (recommended)
const result = await autumn . balances . check ({
customerId: "cus_123" ,
featureId: "ai_tokens" ,
requiredBalance: 1000 ,
sendEvent: true // Atomically check AND track
});
if ( ! result . allowed ) {
return res . status ( 403 ). json ({ error: "Insufficient tokens" });
}
// Usage already tracked, just process request
await processAIRequest ();
Use check with sendEvent: true for atomic check + track operations. This prevents race conditions where usage could be tracked even when the balance is insufficient.
Batch Tracking
For high-volume scenarios, batch multiple track calls:
// Process multiple requests
const requests = await processBatchRequests ( batchData );
// Track all usage in parallel
await Promise . all (
requests . map ( req =>
autumn . balances . track ({
customerId: req . customerId ,
featureId: "api_calls" ,
value: 1 ,
properties: { batchId: batchData . id }
})
)
);
Query Parameters
Bypass cache for fresh balance data.
Expand related objects. Supports: "balance.feature"
Analytics and Reporting
Use the properties field to add context for analytics:
await autumn . balances . track ({
customerId: "cus_123" ,
featureId: "api_calls" ,
value: 1 ,
properties: {
// Request context
endpoint: "/v1/generate" ,
method: "POST" ,
// Feature details
model: "gpt-4" ,
temperature: 0.7 ,
// Performance metrics
responseTime: 1250 ,
tokensGenerated: 500 ,
// User context
userId: "usr_xyz" ,
workspaceId: "ws_abc" ,
// Business context
plan: "enterprise" ,
region: "us-east-1"
}
});
These properties are available in your analytics dashboard and can be queried via the Events API.
Next Steps
Check Access Verify feature access before operations
Set Up Webhooks Listen for threshold and limit events