This architecture measures a multi-tenant SaaS product where people work inside customer accounts. It answers activation, adoption, and retention questions without confusing ten active teammates with ten active customers.
Define the decisions#
Start with four operating questions:
- Which new accounts reach first value?
- How long does activation take?
- Which key features are adopted by each account?
- Which activated accounts return in later weeks?
Assign a product owner to the activation definition. “Activated” should describe a meaningful outcome, not a convenient page view.
Use an account-aware contract#
Create one product_events collection with:
| Event | When it fires | Required properties |
|---|---|---|
account_created |
Company workspace commits successfully | account_id, user_id, plan, source |
data_source_connected |
First valid source is usable | account_id, user_id, source_type |
dashboard_published |
Dashboard becomes available to its audience | account_id, user_id, dashboard_id |
account_activated |
First time the approved activation rule is met | account_id, time_to_activate_seconds |
key_feature_used |
One approved recurring-value action completes | account_id, user_id, feature |
Example:
{
"event": "key_feature_used",
"event_id": "evt_01J...",
"account_id": "acct_7",
"user_id": "usr_42",
"feature": "scheduled_export",
"plan": "pro",
"schema_version": 1
}
Send IDs from your application database, not emails or company names. Attach the account in which the action occurred because one user may belong to several accounts.
Emit server-side facts#
Log completion after the authoritative application transaction. A button click does not prove a data source connected or a dashboard published.
Use an outbox in the same database transaction for account_created and account_activated. A worker sends batches to GraphJSON and keeps a stable event_id across retries. Less critical interaction events can use a lighter queue, but delivery failure must remain observable.
Create separate production and non-production workspaces when access and metrics need a hard boundary.
Make activation explicit#
Suppose activation means connecting a source and publishing a dashboard within seven days of account creation. Emit account_activated once when the application first observes that condition.
This gives the team a stable fact, while a SQL funnel remains useful for diagnosing the path:
WITH per_account AS (
SELECT
JSONExtractString(json, 'account_id') AS account_id,
windowFunnel(7 * 24 * 60 * 60)(
toDateTime(timestamp),
JSONExtractString(json, 'event') = 'account_created',
JSONExtractString(json, 'event') = 'data_source_connected',
JSONExtractString(json, 'event') = 'dashboard_published'
) AS step_reached
FROM product_events
WHERE toDateTime(timestamp) >= subtractDays(now(), 90)
GROUP BY account_id
)
SELECT
countIf(step_reached >= 1) AS created,
countIf(step_reached >= 2) AS connected,
countIf(step_reached >= 3) AS published
FROM per_account
The emitted activation event is convenient for operating metrics; the funnel explains where accounts stop.
Build the operating dashboard#
Create these tiles:
- weekly accounts created
- weekly unique accounts activated
- activation rate by signup cohort
- median and p90 time to activation
- unique active accounts by key feature
- weekly retained activated accounts
- activation by plan and acquisition source
Count unique account_id, not raw events. Keep plan and source definitions event-time consistent. If you need current plan, derive it from explicit subscription state-change events.
Use titles such as “Weekly accounts using scheduled exports,” not “Engagement.”
Add customer-facing analytics safely#
If customers should see their own usage, reuse the durable account_id as an authorization filter. Your server:
- authenticates the user
- confirms membership in the selected account
- chooses an allowlisted report
- adds
account_id = authorized_account - requests a GraphJSON iframe URL
- returns only the URL
Use the production embedded analytics checklist before launch.
Reconcile and govern#
Each day during rollout, compare:
account_createdwith committed account database rowsdata_source_connectedwith active connector recordsdashboard_publishedwith published dashboard recordsaccount_activatedwith an application-side activation calculation
Sample several account journeys end to end. Track missing IDs, unknown feature names, duplicate event IDs, and worker dead letters.
Review the event contract with product, engineering, and customer success when the activation definition changes. Version the event or metric definition rather than silently changing the meaning of an old dashboard.
Launch checklist#
- Activation describes a customer outcome and has one owner.
- Every relevant event includes durable
account_id. - Server completion—not UI intent—emits business facts.
- Important events use an outbox or durable queue.
- Metrics count accounts and users intentionally.
- Activation and source fields have versioned definitions.
- Embedded reports enforce tenant filters on the server.
- Database reconciliation passes for a settled interval.