Skip to main content

Overview

The /check endpoint verifies whether a customer has access to a specific feature or product. Use it to:
  • Gate features behind paywalls
  • Display remaining usage limits
  • Show upgrade prompts when limits are reached
  • Combine check + track in a single atomic operation

Quick Start

import { useCustomer } from '@useautumn/autumn-js/react';

function FeatureButton() {
  const { check } = useCustomer();

  const handleClick = () => {
    const result = check({ featureId: "ai_tokens" });
    
    if (!result.allowed) {
      alert(`AI limit reached. ${result.balance?.remaining || 0} tokens left.`);
      return;
    }
    
    // Proceed with feature
    generateAIContent();
  };

  return <button onClick={handleClick}>Generate AI Content</button>;
}

Parameters

customerId
string
required
The ID of the customer to check access for.
featureId
string
The ID of the feature to check. Use either featureId or productId.
productId
string
The ID of the product to check. Use either featureId or productId.
entityId
string
The ID of the entity for entity-scoped checks (e.g., per-seat limits).
requiredBalance
number
default:"1"
Minimum balance required for access. Returns allowed: false if the customer’s balance is below this value.
// Check if customer has at least 500 tokens
check({ featureId: "api_calls", requiredBalance: 500 })
sendEvent
boolean
default:"false"
If true, atomically records a usage event while checking access. Combines check + track in one call.
// Check AND consume 100 tokens in one atomic operation
check({ 
  featureId: "ai_tokens", 
  requiredBalance: 100,
  sendEvent: true  // Deducts 100 tokens if allowed
})
withPreview
boolean
default:"false"
If true, includes upgrade/upsell information when access is denied. Useful for displaying paywalls.
const result = check({ 
  featureId: "advanced_analytics", 
  withPreview: true 
});

if (!result.allowed && result.preview) {
  // Show upgrade modal with result.preview.products
}
properties
object
Additional properties to attach to the usage event (only used if sendEvent: true).

Response

interface CheckResponse {
  allowed: boolean;              // Whether customer has access
  customerId: string;            // The customer ID
  entityId?: string;             // The entity ID (if entity-scoped)
  requiredBalance?: number;      // The required balance checked
  balance: Balance | null;       // Customer's balance details
  preview?: {                    // Upgrade info (if withPreview: true and !allowed)
    scenario: "usage_limit" | "feature_flag";
    title: string;
    message: string;
    featureId: string;
    featureName: string;
    products: Product[];         // Products that grant access
  };
}

interface Balance {
  featureId: string;
  granted: number;               // Total balance granted
  remaining: number;             // Balance remaining
  usage: number;                 // Current usage
  unlimited: boolean;            // Whether feature is unlimited
  overageAllowed: boolean;       // Whether overage is allowed
  nextResetAt: number | null;    // Timestamp of next reset
  breakdown?: BalanceBreakdown[];  // Detailed balance sources
}

Common Use Cases

Simple Feature Gate

function ExportButton() {
  const { check } = useCustomer();
  
  const handleExport = () => {
    const { allowed } = check({ featureId: "pdf_export" });
    
    if (!allowed) {
      showUpgradeModal();
      return;
    }
    
    exportToPDF();
  };
  
  return <button onClick={handleExport}>Export to PDF</button>;
}

Display Remaining Usage

function UsageMeter() {
  const { check } = useCustomer();
  const result = check({ featureId: "api_calls" });
  
  if (!result.balance) return null;
  
  const { remaining, granted, unlimited } = result.balance;
  
  if (unlimited) {
    return <div>Unlimited API calls</div>;
  }
  
  return (
    <div>
      {remaining} / {granted} API calls remaining
      {remaining < granted * 0.1 && (
        <button onClick={() => upgradePlan()}>Upgrade for more</button>
      )}
    </div>
  );
}

Atomic Check + Track

Use sendEvent: true to check access and record usage in a single atomic operation:
// Check if customer has 1000 tokens AND consume them atomically
const result = await autumn.balances.check({
  customerId: "cus_123",
  featureId: "ai_tokens",
  requiredBalance: 1000,
  sendEvent: true,  // Atomically deduct tokens if allowed
  properties: {
    model: "gpt-4",
    requestId: "req_xyz"
  }
});

if (!result.allowed) {
  return res.status(403).json({ error: "Insufficient tokens" });
}

// Tokens already deducted, proceed with AI generation
await generateAI();

Show Upgrade Options on Limit

function AnalyticsPage() {
  const { check } = useCustomer();
  const result = check({ 
    featureId: "advanced_analytics",
    withPreview: true 
  });
  
  if (!result.allowed && result.preview) {
    return (
      <UpgradePrompt
        title={result.preview.title}
        message={result.preview.message}
        plans={result.preview.products}
      />
    );
  }
  
  return <AdvancedAnalyticsDashboard />;
}

Entity-Scoped Checks (Per-Seat Limits)

// Check if a specific workspace has access
const result = await autumn.balances.check({
  customerId: "cus_123",
  entityId: "workspace_abc",  // Check specific workspace
  featureId: "team_members",
  requiredBalance: 1  // Check if they can add 1 more member
});

if (!result.allowed) {
  return res.status(403).json({ 
    error: "Team member limit reached for this workspace" 
  });
}

await addTeamMember(workspaceId, newMemberId);

Error Handling

import { AutumnClientError } from '@useautumn/autumn-js/react';

try {
  const result = check({ featureId: "api_calls" });
  
  if (!result.allowed) {
    console.log("Access denied", result.balance?.remaining);
  }
} catch (error) {
  // Note: check() in React is local and doesn't throw
  // It uses cached customer data from useCustomer()
}

Balance Breakdown

When a customer has multiple sources of balance (multiple plans, top-ups, rollovers), the breakdown array shows where each portion comes from:
const result = await autumn.balances.check({
  customerId: "cus_123",
  featureId: "api_calls"
});

result.balance?.breakdown?.forEach(item => {
  console.log(`Plan ${item.planId}: ${item.remaining} of ${item.includedGrant} remaining`);
  if (item.reset) {
    console.log(`Resets at: ${new Date(item.reset.resetsAt)}`);
  }
});

Query Parameters

skipCache
boolean
default:"false"
Bypass cache and fetch fresh data from the database.
expand
string[]
Expand related objects. Supports: "balance.feature"
// Include full feature object in response
await autumn.balances.check(
  { customerId: "cus_123", featureId: "api_calls" },
  { expand: ["balance.feature"] }
);

Next Steps

Track Usage

Learn how to record usage events

Handle Upgrades

Upgrade customers when they hit limits

Build docs developers (and LLMs) love