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.
| Source | Example active events |
|---|---|
| SDK | page_view, screen_view, button_click, form_submit, search, login, sign_up, product_viewed, add_to_cart, checkout_started, purchase, order_completed |
| E-commerce platforms | order_placed, order_paid, cart_created, cart_updated, customer_created, refund_requested |
| CRM sync | High-intent contact / lead create + update from HubSpot, Salesforce |
| MMP postbacks | app_installed, deep-link click |
| File upload | Newly 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:
| Source | Example passive events |
|---|---|
| SDK telemetry | session_end, app_backgrounded, product_impression, promotion_viewed, identify, alias, group |
| Channel callbacks | channel_delivery_status, email.delivered, whatsapp.read, push.delivered, sms.failed |
| System-generated | Internal smoke patterns, null-user events, deduped identity-resolution artifacts |
| Bot / crawler traffic | Filtered 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:
- 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. - 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. - 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 dashboard — Settings → 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
- Event ingestion — how events are sent
- Event schema — the six event types
- Receipt events — channel callbacks (classified passive)