Skip to content

Tutorial: Track Usage and Show a Quota Meter

In this tutorial, you’ll report API usage to the SDK, display a quota meter that updates in real time, and trigger a warning placement when usage exceeds 80%. Takes about 15 minutes.

A usage tracking flow that:

  • Reports API call counts to the SDK via updateUsage()
  • Shows a quota meter with current/limit display
  • Triggers a warning banner when usage exceeds 80%
  • Triggers an upgrade modal when usage exceeds 100%
  • A React 18+ application with @revturbine/sdk installed
  • A RevTurbineProvider set up (see Quickstart)
  1. Add usage entitlements and placements to your config

    exported_config.json
    {
    "version": "v1",
    "plans": [
    { "id": "plan_starter", "unique_handle": "starter", "name": "Starter", "tier_position": 0, "sort_order": 0 },
    { "id": "plan_pro", "unique_handle": "professional", "name": "Professional", "tier_position": 1, "sort_order": 1 }
    ],
    "entitlements": [
    { "id": "ent_api_calls", "unique_handle": "api_calls", "name": "API Calls", "type": "usage_limit", "unit": "calls" }
    ],
    "entitlement_rules": [
    {
    "id": "er_api_calls_starter",
    "entitlement_id": "ent_api_calls",
    "targets": [{ "kind": "plan", "id": "plan_starter" }],
    "segment_ids": [],
    "type_fields": { "kind": "usage_limit", "limit_value": 1000, "unit": "calls", "period": "per_month", "enforcement": "hard_block" }
    }
    ],
    "segments": [],
    "content_ui_paths": [],
    "surface_templates": [
    { "id": "usage_counter", "surface_type": "in_page", "fields": [] },
    { "id": "banner_warning", "surface_type": "banner", "fields": [] }
    ],
    "placements": [
    {
    "id": "pl_api_quota_meter",
    "name": "API quota meter",
    "category": "fixed",
    "trigger": { "type": "surface_render", "slot_id": "sidebar_usage" },
    "payloads": [
    {
    "id": "pl_api_quota_meter_p0",
    "target": { "plan_ids": [], "segment_chips": [] },
    "surfaces": [
    {
    "template_id": "usage_counter",
    "fields": { "header": "API Calls", "body": "{{usage_current}} / {{usage_limit}} calls used" },
    "ctas": []
    }
    ],
    "caps": {},
    "status": "active"
    }
    ],
    "order": 0
    },
    {
    "id": "pl_usage_warning",
    "name": "Usage limit approaching",
    "category": "usage_credit_seat",
    "trigger": { "type": "usage_threshold", "entitlement_handle": "api_calls", "threshold_percent": 80 },
    "payloads": [
    {
    "id": "pl_usage_warning_p0",
    "target": { "plan_ids": ["plan_starter"], "segment_chips": [] },
    "surfaces": [
    {
    "template_id": "banner_warning",
    "fields": {
    "header": "Usage limit approaching",
    "body": "You've used {{usage_percent}}% of your API calls this month.",
    "dismissible": "Yes"
    },
    "ctas": [
    { "label": "View Plans", "path": "open_checkout", "config": { "purchase": "professional" } }
    ]
    }
    ],
    "caps": {},
    "status": "active"
    }
    ],
    "order": 1
    }
    ]
    }
  2. Add the quota meter to your sidebar

    src/components/Sidebar.tsx
    import { FixedSurfaceSlot } from '@revturbine/sdk';
    export function Sidebar() {
    return (
    <aside style={{ width: 240, padding: 16 }}>
    <h3>Dashboard</h3>
    <nav>{/* ... nav links ... */}</nav>
    <div style={{ marginTop: 24 }}>
    <FixedSurfaceSlot
    id="sidebar_usage"
    surfaceTemplateIds={['usage_counter']}
    />
    </div>
    </aside>
    );
    }
  3. Add the warning banner

    src/components/DashboardLayout.tsx
    import { MessageSurfaceSlot } from '@revturbine/sdk';
    export function DashboardLayout({ children }) {
    return (
    <div>
    {/* Warning banner appears when usage > 80% */}
    <MessageSurfaceSlot
    id="dashboard_banner"
    surfaceTemplateIds={['banner_warning']}
    />
    <div style={{ display: 'flex' }}>
    <Sidebar />
    <main style={{ flex: 1, padding: 16 }}>
    {children}
    </main>
    </div>
    </div>
    );
    }
  4. Report usage from your API layer

    src/hooks/useApiCall.ts
    import { useRevTurbine } from '@revturbine/sdk';
    import { useCallback, useRef } from 'react';
    export function useApiCall() {
    const { sdk } = useRevTurbine();
    const callCount = useRef(0);
    const apiCall = useCallback(async (endpoint: string, options?: RequestInit) => {
    const response = await fetch(endpoint, options);
    // Report usage to the SDK
    callCount.current += 1;
    sdk?.updateUsage({ api_calls: callCount.current });
    return response;
    }, [sdk]);
    return { apiCall };
    }
  5. Test the flow

    Simulate increasing usage to see the meter and banner update:

    function DemoControls() {
    const { sdk } = useRevTurbine();
    const [calls, setCalls] = useState(0);
    const simulateUsage = (count: number) => {
    setCalls(count);
    sdk?.updateUsage({ api_calls: count });
    };
    return (
    <div style={{ padding: 16, border: '1px solid #ddd', borderRadius: 8 }}>
    <h3>Demo: Simulate API Usage</h3>
    <p>Current: {calls} calls</p>
    <div style={{ display: 'flex', gap: 8 }}>
    <button onClick={() => simulateUsage(500)}>50% (500)</button>
    <button onClick={() => simulateUsage(800)}>80% (800)</button>
    <button onClick={() => simulateUsage(950)}>95% (950)</button>
    <button onClick={() => simulateUsage(1000)}>100% (1000)</button>
    </div>
    </div>
    );
    }

    Expected behavior:

    | Usage | Quota Meter | Warning Banner | |---|---|---| | 500 / 1000 (50%) | Green bar, “500 / 1000” | Hidden | | 800 / 1000 (80%) | Yellow bar, “800 / 1000” | Shows “Usage limit approaching” | | 950 / 1000 (95%) | Red bar, “950 / 1000” | Shows with urgency styling | | 1000 / 1000 (100%) | Full red bar | Shows upgrade modal |

  1. updateUsage() updates the SDK’s internal usage balances
  2. The decision engine re-evaluates all active placements
  3. The quota meter refreshes its display (current/limit)
  4. When usage exceeds the targeting threshold (80%), the warning banner becomes visible
  5. Token expansion fills in {{usage_current}}, {{usage_limit}}, {{usage_percent}}

For more control, emit trigger events:

// When usage approaches limit
if (usagePercent >= 80) {
await sdk.emitTrigger('usage_limit_approaching', {
entitlement_handle: 'api_calls',
current_usage: currentCalls,
usage_limit: 1000,
usage_percent: usagePercent,
});
}
// When usage exceeds limit
if (usagePercent >= 100) {
await sdk.emitTrigger('usage_limit_exceeded', {
entitlement_handle: 'api_calls',
current_usage: currentCalls,
usage_limit: 1000,
});
}

Interactive Playground → Quota Meter