DocsRecipes

Recipes

Account analytics and customer 360

4 min readReviewed July 2026

An account 360 gives product, success, and support teams a shared analytical view of a customer: who the account is, what value its members receive, what commercial lifecycle it is in, and where risk or friction is emerging.

GraphJSON does not automatically create user or account profiles from events. It stores the JSON you send and lets you query it. Build identity, hierarchy, current state, authorization, and profile snapshots deliberately.

This recipe combines the account model in B2B SaaS product analytics with the operational patterns in customer health scoring.

Start with decisions, not a giant profile#

Name the workflows the view supports:

  • prepare for a customer review
  • investigate a reported problem
  • identify incomplete activation
  • understand adoption across teams
  • see renewal context and billing friction
  • decide which account needs proactive outreach

For each workflow, record the minimum required fields and their authority. Avoid copying every CRM field and every event into one unrestricted dashboard.

Establish the account grain#

Choose the durable customer entity:

organization
└── account
    ├── workspace
    └── users

Your commercial contract may be at organization level while product activity occurs in workspaces. Store the relevant IDs:

{
  "event": "workflow_completed",
  "organization_id": "org_3",
  "account_id": "acct_7",
  "workspace_id": "ws_12",
  "user_id": "usr_42",
  "workflow": "monthly_close",
  "result": "success"
}

Do not infer an account by email domain. Contractors, subsidiaries, personal addresses, and domain changes make that mapping unreliable.

Maintain account merges, splits, and workspace transfers in an authoritative identity history. See Identity stitching and account lifecycle.

Assign authority by domain#

Domain Typical authority GraphJSON role
Product behavior Application service Analyze terminal product facts
Plan and entitlement Billing/application database Analyze state transitions or snapshots
Contract and owner CRM Display selected synchronized context
Ticket status Support platform Analyze lifecycle events
Reliability Service telemetry Correlate bounded account impact
Health score Versioned analytical job Display explanation and trend

Never write a new entitlement, contract state, or ticket status back from an analytics query. GraphJSON is the analytical projection, not the transaction system.

Model facts, transitions, and snapshots#

Use immutable events for completed behavior:

{
  "event": "data_source_connected",
  "account_id": "acct_7",
  "connector_type": "warehouse",
  "result": "success",
  "event_id": "connect:job_91"
}

Use transitions for lifecycle changes:

{
  "event": "account_stage_changed",
  "account_id": "acct_7",
  "previous_stage": "onboarding",
  "stage": "adopted",
  "effective_at": "2026-07-25T19:00:00Z",
  "source": "crm",
  "source_record_version": 184
}

Use snapshots for current profile fields that cannot be reconstructed safely:

{
  "event": "account_profile_snapshot",
  "account_id": "acct_7",
  "snapshot_at": "2026-07-26T00:00:00Z",
  "profile_version": 3,
  "plan": "business",
  "lifecycle_stage": "adopted",
  "employee_band": "100_499",
  "region": "na",
  "renewal_month": "2026-11",
  "owner_team": "enterprise_west"
}

Prefer categories over names, emails, contract text, or private notes. Keep sensitive source details behind the source system’s own authorization.

Preserve event-time context#

Account properties change. Decide whether a question needs:

  • state at the time of the event
  • state at the reporting cutoff
  • latest known state

For historical plan analysis, capture a stable plan code on the billing transition or perform an as-of join against state history. A latest-profile join can make old activity appear to have happened under today’s plan.

Label current-state segmentation clearly:

Historical workflows, grouped by current plan

That can be useful, but it is not “workflows by plan at event time.”

Build a bounded profile query#

Select one latest profile per account:

WITH profiles AS (
  SELECT
    JSONExtractString(json, 'account_id') AS account_id,
    argMax(JSONExtractString(json, 'plan'), timestamp) AS plan,
    argMax(JSONExtractString(json, 'lifecycle_stage'), timestamp) AS lifecycle_stage,
    argMax(JSONExtractString(json, 'region'), timestamp) AS region,
    max(toDateTime(timestamp)) AS profile_updated_at
  FROM account_profiles
  WHERE JSONExtractString(json, 'event') = 'account_profile_snapshot'
  GROUP BY account_id
),
activity AS (
  SELECT
    JSONExtractString(json, 'account_id') AS account_id,
    uniqExact(JSONExtractString(json, 'user_id')) AS active_users_28d,
    countIf(JSONExtractString(json, 'event') = 'workflow_completed') AS workflows_28d,
    max(toDateTime(timestamp)) AS last_activity_at
  FROM product_events
  WHERE toDateTime(timestamp) >= now() - INTERVAL 28 DAY
  GROUP BY account_id
)
SELECT
  profiles.account_id,
  profiles.plan,
  profiles.lifecycle_stage,
  profiles.region,
  activity.active_users_28d,
  activity.workflows_28d,
  activity.last_activity_at,
  profiles.profile_updated_at
FROM profiles
LEFT JOIN activity USING account_id

This query is an analytical view. Add billing, support, or reliability fields only when their grain and freshness are equally explicit.

Measure account journeys across members#

An account funnel may be completed by several people:

owner creates account
  → administrator connects data
  → analyst builds report
  → executive views result

Group funnel progress by account_id, not user_id, unless the question is specifically about one person’s journey. Preserve the acting user role so teams can diagnose who is blocked without treating each member as a separate customer.

State whether steps must occur in order, within a window, and after account creation. Use funnel analysis for the SQL pattern.

Separate timeline, summary, and score#

Build three complementary surfaces:

  1. Profile: bounded latest facts and freshness.
  2. Timeline: selected product, billing, support, and reliability evidence.
  3. Scorecard: outcomes, trends, and an explainable health version.

Do not collapse them into one opaque score. An operator should be able to see why a score changed and open the underlying events.

For a secure timeline implementation, use Customer support account timelines.

Enforce tenant and operator authorization#

Never expose a free-form SQL endpoint to a browser. Put the Data API call behind your server:

operator or customer request
  → authenticate
  → authorize requested account
  → select allowlisted report
  → bind validated account ID
  → query GraphJSON
  → return bounded fields

Cache by report version, authorized account, role, and filters. A shared cache key can leak one customer’s data to another. See the Data API frontend reference.

Make freshness visible#

Show:

  • latest event occurrence time
  • profile snapshot time
  • source completeness time
  • health calculation time
  • expected refresh cadence

“No activity” is ambiguous when the pipeline is delayed. Monitor missing snapshots, source lag, unmapped IDs, and accounts whose hierarchy cannot be resolved.

Launch checklist#

  • The customer, account, workspace, and user grains are documented.
  • Every field has a named authority and freshness expectation.
  • Historical questions use deliberate event-time state.
  • Profiles exclude unnecessary personal and sensitive data.
  • Account funnels allow cross-member completion where intended.
  • Timeline, profile, and health score remain distinguishable.
  • Server authorization scopes every account-level request.
  • Caches include tenant, report version, and relevant filters.
  • Missing or stale source data is visible to operators.
  • Critical totals reconcile with CRM, billing, and support authorities.
Need a hand?

Tell us what you’re building and we’ll point you in the right direction.

Contact support