Skip to main content

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

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

customerId
string
required
The ID of the customer whose usage to track.
featureId
string
The ID of the feature to track. Use either featureId or eventName.
eventName
string
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 }
})
entityId
string
The ID of the entity for entity-scoped tracking (e.g., per-workspace usage).
value
number
default:"1"
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 })
properties
object
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

skipCache
boolean
default:"false"
Bypass cache for fresh balance data.
expand
string[]
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

Build docs developers (and LLMs) love