Goal
- Resolve discrepancies between MRR (Monthly Recurring Revenue) numbers on bi.holistics.io and Zoho Billing, and establish a single source of truth for customer MRR.
- Stakeholders: Data Team, RevOps (Quinn, Arden, Vincent), Finance (Sriram).
- Why it matters: MRR is the core metric for revenue planning (see [[Lead Funnel by Sales Motion]]’s $330K target). If the number is wrong, every downstream decision — pricing analysis, churn measurement, forecasting — is wrong.
- Decision-making questions:
- Why does MRR on bi.holistics.io differ from Zoho Billing?
- Which exchange rate methodology should we use for multi-currency subscriptions?
- How do we handle customers with multiple tenants or multi-region setups?
MRR Definition
- MRR = sum of all active subscriptions’ monthly recurring revenue.
- Zoho’s base currency: Singapore Dollar (SGD).
- Per-subscription calculation:
subscription.mrr / subscription.exchange_rate - Customer = a HubSpot Company (one company can have multiple Holistics tenants and multiple Zoho subscriptions).
- Primary Tenant: for customers with multi-region or multi-tenant setups, the pipeline must select one primary tenant. This is the core identity resolution challenge.
Data Architecture
flowchart TD subgraph Sources["Data Sources"] Holistics["Holistics Backend DB\n(Tenants, Users, Trial Submissions)"] Zoho["Zoho Billing\n(Subscriptions, Invoices, Exchange Rates)"] HubSpot["HubSpot CRM\n(Companies)"] end subgraph Pipeline["dbt Pipeline"] stg_tenants["stg_holistics__tenants"] stg_users["stg_holistics__users"] stg_trials["stg_holistics__trial_submissions"] stg_zoho["stg_zoho__subscriptions"] stg_hs["stg_hubspot__companies"] domain["Domain Mapping\n(tenant → domain_name)"] customer_id["itg_mappings__customer_identities\n(Holistics ↔ Zoho ↔ HubSpot)"] dim_customer["dim_customers\n(primary tenant selection)"] fct_mrr["fct_mrr\n(MRR calculation)"] end subgraph Issues["🔴 Known Issues (9 problems)"] I1["1.1/1.2: Exchange Rate Errors\n(static rate, multi-step conversion)"] I2["2.1-2.4: Wrong Tenant↔Company Mapping\n(region bug, domain mismatch,\nduplicates, multi-domain)"] I3["3.1/3.2: Wrong Tenant↔Zoho Mapping\n(manual errors, multi-tenant sub)"] I4["4: Active but Unpaid Customers\n(Zoho subscription logic bug)"] end subgraph Output["Reports"] bi_h["bi.holistics.io\nMRR Overview Dashboard"] monitor["Customer Identity\nMonitoring Dashboard"] end Holistics --> stg_tenants & stg_users & stg_trials Zoho --> stg_zoho HubSpot --> stg_hs stg_tenants & stg_users & stg_trials --> domain domain --> customer_id stg_hs --> customer_id stg_zoho --> customer_id customer_id --> dim_customer dim_customer --> fct_mrr fct_mrr --> bi_h customer_id --> monitor I1 -.->|affects| fct_mrr I2 -.->|affects| customer_id I3 -.->|affects| customer_id I4 -.->|affects| stg_zohoData Sources
- HubSpot CRM: Companies.
- Holistics Backend DB: Tenants, Users, Trial Submissions.
- Zoho Billing: Zoho Customers, Subscriptions, Payments, Invoices, Exchange Rates.
dbt Pipeline Flow
- Staging:
stg_holistics__tenants,stg_holistics__users,stg_zoho__subscriptions,stg_hubspot__companies - Mapping:
itg_mappings__customer_identities(Holistics ↔ Zoho ↔ HubSpot) - Dimensions:
dim_customers(primary tenant selection logic) - Facts:
fct_mrr(final MRR calculation)
- Staging:
Identity Resolution
- Currently relies on
domain_name(extracted from tenant uname or email). This is fragile. - Proposed long-term fix: a canonical
customer_skeyshared across HubSpot and Zoho.
- Currently relies on
ER Diagram
Key Datasets & Dashboards
- Datasets:
- Dashboards:
- MRR Overview: https://bi.holistics.io/dashboards/v4/28300-0-mrr-overview
- Customer Identity Monitoring: https://bi.holistics.io/dashboards/v4/45480-customer-identity-monitoring
- MRR Bucket Breakdown: https://bi.holistics.io/dashboards/v4/41188-adhoc-mrr-bucket-breakdown
Exchange Rate Decision
- Base currency: SGD (Zoho uses only 1).
- Reporting currency: USD (agreed upon, >40% of revenue is in USD, USD is more recognizable and comparable).
- Problem: pipeline previously used static rates; Zoho uses per-transaction rates set at plan creation time.
- Agreed approach (Option 2): use live rates (e.g., Google Finance / ECB) — specifically the current/today’s rate for a “what is it worth now” view.
- Caveat: requires careful handling to avoid FX noise in historical growth metrics.
Identified Problems & Root Causes
Category Problem Root Cause Exchange Rate 1.1/1.2: Inaccuracies Pipeline uses static rates; Zoho uses per-transaction rates set at plan creation HubSpot Mapping 2.1–2.4: Mapping Bugs Missing regions, domain mismatches (e.g., raft.aivsvector.ai), duplicate HubSpot companiesZoho Mapping 3.1/3.2: Link Errors Manual errors and system inability to handle multi-tenant subscriptions Subscription Logic 4: Unpaid Actives Zoho fails to re-activate tenants after past-due payments are settled
Edge Cases
- Multi-tenant customers: Datacubed — one Zoho account for two tenants.
- Multi-region conflicts: Kognity — SG database shows expired trial while US database shows active paying status.
- Domain mismatches: Vector AI — HubSpot uses
vector.ai, app usesraft.ai.
Technical Fixes
- PR #812: Fixed missing regions in manual mapping.
- PR #852: Implemented hard-coded patches for edge cases:
map_holistics_zoho.csv: maps specific tenants (e.g., Datacubed) to shared Zoho accounts.partner_programs.csv: excludes freemium plan IDs from MRR.dom_holistics__internal_tenants.sql: filters out internal Holistics testing tenants.
Progress
- DONE Identify root causes of MRR discrepancies
- DONE Agree on exchange rate methodology (Option 2 — live rates)
- DONE Fix missing regions in manual mapping (PR #812)
- DONE Implement hard-coded patches for edge cases (PR #852)
- DONE Set up Customer Identity Monitoring dashboard
- TODO Replace static exchange rates with live rate ingestion — DAT-576 (for quick win)
- TODO Implement canonical
customer_skeyfor long-term identity resolution - TODO Handle Zoho re-activation bug for past-due subscriptions
References
/ [Internal] Customers' MRR (Exchange Rate)
Created Mon, 25 May 2026 00:00:00 +0000
Modified Mon, 25 May 2026 06:02:25 +0000