Skip to Content
DevelopersEventsMAU classification

MAU classification

Active Reach bills based on Monthly Active Users (MAU) — the number of unique contacts (identified or anonymous) who generated a qualifying active event in a calendar month. The classifier is called Honest Active User (HAU) and the ClickHouse materialized view that backs the MAU count excludes a canonical list of passive event names so Aegis-generated telemetry never inflates the quota.

What counts toward MAU (active events)

A contact becomes an MAU when they generate any event the classifier puts in the ACTIVE category. Active = user-intent action.

SourceExample active events
SDKpage_view, screen_view, button_click, form_submit, search, login, sign_up, product_viewed, add_to_cart, checkout_started, purchase, order_completed
E-commerce platformsorder_placed, order_paid, cart_created, cart_updated, customer_created, refund_requested
CRM syncHigh-intent contact / lead create + update from HubSpot, Salesforce
MMP postbacksapp_installed, deep-link click
File uploadNewly imported contacts (counted once per import)

What does NOT count (passive events)

Anything in the classifier’s *_PASSIVE sets — currently 106 event names across SDK, e-commerce, CRM, analytics, channel callbacks (email / WhatsApp / SMS / RCS / push), Aegis-native telemetry, file upload, and warehouse sync. Examples:

SourceExample passive events
SDK telemetrysession_end, app_backgrounded, product_impression, promotion_viewed, identify, alias, group
Channel callbackschannel_delivery_status, email.delivered, whatsapp.read, push.delivered, sms.failed
System-generatedInternal smoke patterns, null-user events, deduped identity-resolution artifacts
Bot / crawler trafficFiltered by user-agent detection

The full list is the union of every *_PASSIVE set in the MAUEventClassifier, exposed via MAUEventClassifier.all_passive_event_names(). A drift guard test asserts the ClickHouse MV’s exclusion list stays byte-aligned with this Python set — changes to one fail loudly if the other isn’t updated.

How the count is computed (v2.0)

Three pieces work together:

  1. Anon-inclusive identity key. The MAU worker counts unique contacts via coalesce(nullIf(customer_id, ''), anonymous_id), so anonymous storefront sessions count once-per-anon even if they never identify.
  2. Classifier-aligned MV. The events materialized view excludes every name returned by all_passive_event_names() at write time. Aegis-generated callbacks (push_delivered, email_sent, channel_delivery_status, dot-namespaced channel events) never enter the quota.
  3. HAU filter at read time. The MAU SQL applies the same passive filter when computing the monthly Honest Active User count, so the operator-facing number matches the billed number.

This is what closed a previously known case where a low-traffic storefront showed 1 MAU against 385 events — almost all of the events were channel_delivery_status callbacks that should never have counted.

Ghost-user filtering

In addition to the passive exclusion above, the classifier filters:

  • Smoke / synthetic patterns (Aegis-internal probes)
  • Events with null / empty user keys
  • Contacts marked deleted

You can see the filtered-vs-counted breakdown in Settings → Billing → MAU Transparency.

How to optimize MAU

  • Deduplicate before import — merge contacts with the same email/phone before uploading CSVs
  • Use identify() early — anonymous SDK sessions still count once per anon if they fire active events
  • Clean inactive contacts — contacts who haven’t engaged in 90+ days can be archived
  • Review the transparency dashboardSettings → Billing → MAU Transparency shows exactly which contacts counted and why

Billing tiers

MAU pricing varies by plan. Check your current plan and MAU usage in Settings → Billing → Overview. See active-reach.ai/pricing  for current tier details.

What’s next