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.
What You’ll Build
Section titled “What You’ll Build”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%
Prerequisites
Section titled “Prerequisites”- A React 18+ application with
@revturbine/sdkinstalled - A
RevTurbineProviderset up (see Quickstart)
-
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}]} -
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 }}><FixedSurfaceSlotid="sidebar_usage"surfaceTemplateIds={['usage_counter']}/></div></aside>);} -
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% */}<MessageSurfaceSlotid="dashboard_banner"surfaceTemplateIds={['banner_warning']}/><div style={{ display: 'flex' }}><Sidebar /><main style={{ flex: 1, padding: 16 }}>{children}</main></div></div>);} -
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 SDKcallCount.current += 1;sdk?.updateUsage({ api_calls: callCount.current });return response;}, [sdk]);return { apiCall };} -
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 |
How It Works
Section titled “How It Works”updateUsage()updates the SDK’s internal usage balances- The decision engine re-evaluates all active placements
- The quota meter refreshes its display (current/limit)
- When usage exceeds the targeting threshold (80%), the warning banner becomes visible
- Token expansion fills in
{{usage_current}},{{usage_limit}},{{usage_percent}}
Triggering Programmatic Alerts
Section titled “Triggering Programmatic Alerts”For more control, emit trigger events:
// When usage approaches limitif (usagePercent >= 80) { await sdk.emitTrigger('usage_limit_approaching', { entitlement_handle: 'api_calls', current_usage: currentCalls, usage_limit: 1000, usage_percent: usagePercent, });}
// When usage exceeds limitif (usagePercent >= 100) { await sdk.emitTrigger('usage_limit_exceeded', { entitlement_handle: 'api_calls', current_usage: currentCalls, usage_limit: 1000, });}Try It Live
Section titled “Try It Live”Next Steps
Section titled “Next Steps”- Entitlements Guide — all entitlement types
- Events & Analytics — trigger events and tracking
- Component Gallery — interactive demos of all built-in slot components