Sign in →
Embed Widgets1 min read

AforoUsageMeter

Real-time per-metric usage gauges with quota visualization, threshold detection (70/90/100%), and period-progress. Polls every 60s by default; emits threshold_reached events the parent page handles.

Updated 2026-06-15Suggest edits
Docs Embed Widgets UsageMeter
Live preview — synthetic data
AforoUsageMeter
Per-metric usage gauges with 70/90/100% threshold detection.
Loads the widget from https://embed-demo.aforo.ai with synthetic data.

What it renders#

A grid of progress bars showing current-period usage vs quota for each metric on the customer's active subscription.

  • Per-metric: name, used / quota figures, progress bar (0–100%), status pill (text + color)
  • Color thresholds: green ≤70%, amber 70–90%, red ≥90% — color is supplementary; the status pill always carries text alternative
  • Optional period-progress strip showing percent of the billing cycle elapsed
  • Approximation note: when any metric's pricing model is tiered or allowance-based, the displayed value carries an upper-bound footnote (P14' revenue-estimation contract)
  • Unlimited quota handling: metrics with quota = 0 render "Unlimited" (no progress bar)
  • Polls every 60s by default; configurable via prop
INFO
UsageMeter is read-only. Threshold crossings emit aforo.usage-meter.threshold_reached events the parent page can handle to show upgrade prompts or send notifications. Threshold detection fires once per metric per threshold per mount — re-mounting the widget re-arms the detector.
From Operator Portal → Settings.
From Operator Portal → Embed Studio → Keys.
Minted by your backend per customer session — see Authentication.
Script tag
<div data-aforo-widget="usage-meter"
     data-aforo-tenant-slug="your-tenant"
     data-aforo-embed-key="ek_live_replace_me"
     data-aforo-bridge-token="bridge.replace.me"
     data-aforo-subscription-id="sub_xxx"
     data-aforo-mode="expanded"></div>

<script src="https://cdn.aforo.ai/embed/loader.js" async></script>
NPM / React
import { AforoUsageMeter } from '@aforoai/storefront-widgets';

export function Pricing() {
  return (
    <AforoUsageMeter
      tenantSlug="your-tenant"
      embedKey="ek_live_replace_me"
  bridgeToken="bridge.replace.me"
  subscriptionId="sub_xxx"
  mode="expanded"
    />
  );
}
Preview with my account
Fill in Tenant slug above to enable this preview.

Mount examples#

app/account/usage/page.tsx
import { AforoUsageMeter } from '@aforoai/storefront-widgets';

export default async function UsagePage() {
  const session = await getSession();
  const bridgeToken = await mintBridgeToken({
    externalId: session.customerId,
    email: session.email,
  });

  return (
    <AforoUsageMeter
      tenantSlug="acme"
      embedKey="embk_live_…"
      bridgeToken={bridgeToken}
      mode="expanded"
      pollIntervalMs={60000}
    />
  );
}

Props reference#

PropTypeDefaultDescription
tenantSlugstring(required)Your tenant slug
embedKeystring(required)Embed key from Embed Studio
bridgeTokenstring(required)Short-lived JWT minted server-side per customer
subscriptionIdstring?Specific subscription. Omit to use customer's default subscription
mode"compact" | "expanded""expanded"Layout density
pollIntervalMsnumber?60000Polling cadence (min 10000)
showPeriodProgressboolean?trueRender the period-elapsed strip
theme"auto" | "light" | "dark""auto"Color scheme
themeOverridesPartial<ThemeTokens>?Per-widget theme tuning
localestring?navigator.languageBCP-47 locale for number formatting

Compact vs expanded mode#

Compact renders a single horizontal strip with abbreviated metric names and small progress bars — appropriate for a header bar, sidebar widget, or notification rail. Expanded renders a 2-column grid (auto-stacks on narrow viewports) with full metric names, current vs quota figures, and progress bars sized for primary focus.

PRO TIP
Both modes apply the same threshold colors. The structural difference is information density — compact suits ambient awareness, expanded suits a dedicated usage dashboard.

Thresholds#

The widget tracks three threshold crossings per metric: 70% (warning), 90% (critical), and 100% (exceeded). Crossings fire once per metric per threshold per mount — re-mounting the widget re-arms the detector. Each crossing emits an aforo.usage-meter.threshold_reached event with a typed payload.

parent.ts
AforoEmbed.on('aforo.usage-meter.threshold_reached', (e) => {
  // e.payload = {
  //   subscriptionId: 'sub_default',
  //   metricName: 'api_calls',
  //   threshold: 90,            // 70 | 90 | 100
  //   quotaUsedPct: 95,         // current actual percentage
  //   tenantId: 'acme',
  //   customerId: undefined,    // never leaked
  // }
  if (e.payload.threshold === 90) {
    showUpgradeBanner(e.payload.metricName);
  } else if (e.payload.threshold === 100) {
    showHardLimitWarning(e.payload.metricName);
  }
});

Events#

See the for the full event vocabulary. UsageMeter emits:

  • aforo.usage-meter.ready — once on first paint
  • aforo.usage-meter.threshold_reached — once per metric per threshold per mount (see Thresholds above)
  • aforo.usage-meter.error — fetch failure with typed code (never PII)
INFO
Per FR-SEC-23, threshold detection is advisory only — the authoritative signal for over-quota state is the server-side webhook, not the parent-page event. Use the event to drive UI hints; never use it as a hard-limit gate.

Limitations#

  • Polling, not SSE (yet). Today the widget polls every pollIntervalMs (default 60s). Real-time SSE push lands in a subsequent release; until then, the 60s cadence is a sensible default for live-feeling usage without hammering the BFF.
  • Approximation footnote is fixed. The widget shows a single combined footnote when ANY metric uses tiered or allowance-based pricing. Per-metric flags are Phase 1.
  • No magic-link fallback yet. UsageMeter requires a bridge token. Magic link parity with InvoiceList lands in a subsequent minor.
  • No historical chart. The widget shows current-period usage only. A per-metric sparkline showing the last N billing cycles is filed as a follow-up.