The multi-system data conflict problem

Every SaaS company running Salesforce, Stripe, and an analytics platform faces the same structural problem: each system records a different version of the truth, updates at a different frequency, and uses different definitions for the same concepts.

Stripe records what was actually billed. Subscriptions, invoices, payment successes, payment failures, refunds, and prorations. Revenue in Stripe is transactional reality.

Salesforce records what was agreed during the sales process. Deal values, contract terms, account ownership, and renewal dates. Revenue in Salesforce is commercial intent.

Analytics platforms (Mixpanel, Amplitude, Segment) record what users actually do. Feature usage, session frequency, event volumes, and engagement patterns. Activity in analytics is behavioural reality.

These systems weren't designed to agree. When your CFO asks for ARR, the answer depends on which system you check. When your CS team asks whether an account is healthy, the CRM says one thing and product usage says another. When billing and the CRM disagree on whether a customer expanded, someone has to investigate manually.

Eru's data layer sits across all three system types. Rather than replacing your systems or adding another silo, Eru ingests events from each source, normalises them into a unified entity model, detects conflicts automatically, and surfaces discrepancies with root causes before they become board-level problems.

Architecture: how Eru's data layer normalises cross-system metrics

Eru's pipeline architecture is designed around three principles: each integration is independent, every event is processed idempotently, and conflict resolution follows explicit, configurable rules.

Integration layer: independent pipelines per source

Each connected system — Stripe, Salesforce, HubSpot, Snowflake, Amplitude, Intercom — runs as an independent ingestion pipeline. Pipelines connect via OAuth or API keys, receive events through webhooks or polling, and maintain their own health status, retry queues, and checkpoint state.

This isolation is intentional. A Stripe webhook outage doesn't affect your Salesforce data. An expired HubSpot OAuth token doesn't delay billing reconciliation. Each pipeline has its own circuit breaker: if a source system returns errors, Eru retries with exponential backoff (5-second initial delay, 5-minute maximum, 10 retry attempts) before opening the circuit and alerting your team.

The integration layer diagram describes the flow: source systems (Stripe, Salesforce, Analytics) each connect via independent webhook listeners and API pollers into separate ingestion queues. Each queue feeds through an event deduplication layer (using event IDs to ensure exactly-once processing) into the normalisation engine. Failed events route to a dead-letter queue with automatic retry scheduling and alerting.

Normalisation engine: one entity, one truth

Raw events from different systems use different identifiers, schemas, and definitions. Eru's normalisation engine transforms them into a unified entity model:

  • Entity resolution: Maps Stripe customer_id, Salesforce Account ID, and analytics organization_id to a single canonical customer entity using probabilistic matching across names, emails, domains, and custom metadata fields. Match confidence scores are visible and adjustable.
  • Schema alignment: Normalises field names, data types, and value formats across systems. A "subscription amount" in Stripe, an "opportunity value" in Salesforce, and a "plan tier" in your product database are mapped to consistent revenue and plan attributes on the canonical entity.
  • Metric calculation: Applies identical calculation logic across sources. MRR from Stripe divides annual subscriptions by 12, strips tax if excluded in other systems, and converts currencies at consistent daily rates. The same logic applies to Salesforce opportunity values. This eliminates false-positive conflicts caused by calculation methodology differences.
  • Temporal alignment: Normalises timestamps to UTC and accounts for system-specific timing patterns. Stripe events are real-time. Salesforce updates may batch overnight. Analytics events may arrive with 1–4 hour lag. Eru tracks data freshness per-source and per-entity, so every metric carries a "last updated" timestamp from each contributing system.

The normalisation architecture diagram illustrates the transformation: raw events from each source pass through entity resolution (probabilistic matching), then schema alignment (field normalisation), then metric calculation (unified formulas), producing canonical entity records with per-source timestamps and confidence scores stored in Eru's knowledge graph.

Conflict detection engine: when systems disagree

Once data is normalised, Eru continuously compares values across sources for each entity. The conflict detection engine runs on every data update and applies configurable rules to classify discrepancies.

The conflict detection pipeline compares normalised values from each source against a configurable tolerance threshold. When values differ beyond the threshold, the engine classifies the conflict by type, traces the root cause to specific events, and routes the alert to the appropriate team with full context.

Conflict resolution: what happens when CRM and billing disagree on ARR

ARR discrepancies between Stripe and Salesforce are the most common and most consequential data conflict in B2B SaaS. Eru classifies every CRM-billing conflict into one of four categories, each with different resolution logic:

1. Billing timing mismatch

What it looks like: Stripe shows a subscription renewal on March 28. Salesforce shows the renewal close date as April 2. For a 5-day window, the two systems disagree on whether this revenue belongs to March or April.

How Eru resolves it: Eru detects that the underlying event is the same (a renewal for the same account at the same amount) and flags it as a timing mismatch rather than a true discrepancy. The alert includes both dates and the dollar impact. If your tolerance is set to allow 7-day timing variance, this resolves automatically. If the gap exceeds your tolerance, it escalates with the full timeline.

Typical prevalence: 5–15% of accounts at any month boundary.

2. Expansion revenue gap

What it looks like: A customer self-service upgrades via your billing portal. Stripe records the subscription change immediately. Salesforce has no corresponding opportunity or contract amendment because the upgrade bypassed the sales process entirely.

How Eru resolves it: Eru detects a Stripe customer.subscription.updated event with an increased amount and no matching Salesforce opportunity within a configurable window (default: 14 days). The alert surfaces the account, old and new MRR, upgrade date, and flags it as expansion revenue missing from the CRM. This prevents the situation where Stripe accurately reflects ARR growth but your CRM-based board report doesn't.

Typical prevalence: 10–30% of expansion revenue missing from CRM at companies with self-service upgrade flows.

3. Failed payment drift

What it looks like: A customer's credit card expires. Stripe retries the charge on a schedule (typically 3 attempts over 7–21 days). After all retries are exhausted, Stripe cancels the subscription. But Salesforce still shows the account as "Active" because nobody updated the CRM.

How Eru resolves it: Eru tracks the full Stripe payment retry lifecycle. When a charge.failed event occurs, Eru flags the account as at-risk. When retries are exhausted and the subscription is cancelled (customer.subscription.deleted), Eru checks the Salesforce account status. If Salesforce still shows "Active" or "Customer", Eru raises a high-severity alert: this is phantom revenue in your CRM that no longer exists in billing.

Typical prevalence: 2–5% of reported MRR as phantom revenue in the CRM.

4. MRR recognition difference

What it looks like: Stripe calculates MRR as the sum of active subscription line items divided by their billing intervals. Salesforce calculates MRR from opportunity amounts entered by sales reps, who may have included one-time setup fees, multi-year totals, or estimated usage-based components.

How Eru resolves it: Eru applies normalisation rules that strip known calculation differences: divides annual amounts by 12, excludes one-time line items, converts multi-year contracts to monthly values, and uses consistent exchange rates. After normalisation, remaining discrepancies represent genuine data quality issues rather than methodology differences. This eliminates the class of false positives that makes manual reconciliation so exhausting.

Typical prevalence: 15–40% of accounts before normalisation; 2–8% after normalisation.

Data freshness guarantees by integration type

Different source systems update at different rates. Eru tracks data freshness per integration and per entity, so every metric carries a clear indication of how current it is.

Integration Ingestion Method Typical Freshness Staleness Alert Threshold
Stripe Webhook (real-time events) <30 seconds 5 minutes
Salesforce Streaming API + polling <2 minutes 15 minutes
HubSpot Webhook + polling <5 minutes 30 minutes
Snowflake / BigQuery Scheduled query (configurable) 1–4 hours (depends on schedule) 2× configured interval
Amplitude / Mixpanel Export API (hourly batch) 1–2 hours 4 hours
Intercom / Zendesk Webhook + polling <5 minutes 30 minutes

When data from any source exceeds its staleness threshold, Eru marks all metrics that depend on that source with a freshness warning. If Stripe webhooks haven't arrived in 5 minutes, revenue metrics show "Stripe data may be stale — last event at [timestamp]". If Salesforce polling hasn't returned new data in 15 minutes, account ownership and deal stage metrics are flagged.

This prevents the worst-case scenario: acting on metrics that appear current but are actually hours or days old because a pipeline silently failed.

Error handling for sync failures

Eru's pipeline reliability model is built around the assumption that failures are normal. External APIs go down, rate limits get hit, OAuth tokens expire, and webhook endpoints receive duplicate or out-of-order events. The error handling system is designed to handle all of these without losing data or producing incorrect metrics.

Retry logic with exponential backoff

When an API call or webhook processing fails, Eru retries with exponential backoff:

Rate limit errors (HTTP 429) are handled separately: Eru reads the Retry-After header and waits the specified duration before retrying. This prevents cascading rate limit violations that can occur with fixed retry schedules.

Circuit breaker pattern

If a source system fails consistently (10 consecutive failures or 50% failure rate over a 5-minute window), the circuit breaker opens. While open:

Every 60 seconds, the circuit breaker allows a single probe request. If the probe succeeds, the circuit closes and queued events are processed in order. This prevents a recovering API from being overwhelmed by a burst of retried requests.

Idempotent event processing

Stripe and other webhook-based systems may deliver the same event multiple times. Eru deduplicates events using a combination of event ID and source system identifier. If a customer.subscription.updated event with ID evt_1234 arrives twice, the second delivery is acknowledged and discarded. This prevents duplicate revenue records, double-counted metric changes, and phantom discrepancies.

Dead-letter queue and manual review

Events that fail all retry attempts are moved to a dead-letter queue. Each dead-letter entry includes the original event payload, all error messages from retry attempts, and the affected entity. Your team can review dead-letter events in the Eru dashboard, reprocess them after fixing the underlying issue, or dismiss them if the event is no longer relevant.

Alerting workflows for data drift

Eru's alerting system is designed to surface the right information to the right team at the right time. Alerts are routed based on severity, affected system, and discrepancy type.

Alert severity levels

Severity Trigger Default Routing Expected Response Time
Critical Pipeline down >15 min, phantom revenue >1% of MRR, entity resolution failure rate >10% Slack DM to pipeline owner + channel alert <1 hour
High Single-account ARR discrepancy >$5K, expansion revenue gap >$2K not resolved in 7 days Slack channel alert <24 hours
Medium Account-level discrepancy >5% of account MRR, data freshness warning on critical integration Slack channel + weekly digest <1 week
Low Timing mismatches within tolerance, entity resolution below 95% confidence Weekly digest only Next review cycle

Alert content

Every alert includes full context so your team can act without switching between tools:

  • Account name and ID from each affected system
  • Discrepancy type (timing mismatch, expansion gap, failed payment drift, recognition difference)
  • Values from each source: Stripe MRR, Salesforce MRR, and the delta
  • Root cause analysis: The specific events that created the discrepancy
  • Suggested resolution: What needs to be updated, and in which system
  • Timeline: When the discrepancy was first detected and whether it's growing or stable

Slack integration

Eru routes alerts to Slack channels with configurable routing rules. A typical setup:

  • #revenue-alerts — Critical and high-severity billing discrepancies for finance and RevOps
  • #pipeline-health — Integration failures, data freshness warnings, circuit breaker events for the data team
  • #cs-risk-signals — Failed payment drift and account health discrepancies for customer success

Alerts are actionable from Slack: team members can acknowledge, snooze, or dismiss alerts directly from the message. Acknowledged alerts track who is investigating and update the thread when the discrepancy is resolved.

Troubleshooting: common data conflict issues

The three most common issues teams encounter when reconciling data across CRM, billing, and analytics systems — and how Eru handles each one.

Duplicate revenue records

Symptom: Total MRR in your dashboard is higher than expected. Individual account MRR values look correct, but the aggregate is inflated.

Common causes:

How Eru prevents this: Eru's entity resolution layer maps all subscriptions and opportunities to canonical entities. When two Stripe subscriptions map to the same canonical customer, Eru evaluates whether they represent genuinely separate revenue streams (e.g., a customer with both a platform subscription and an add-on) or duplicates (same product, overlapping dates). Duplicates are flagged with both subscription IDs and a recommendation to resolve. Idempotent event processing ensures webhook retries never create phantom records.

Currency conversion discrepancies

Symptom: A European customer's MRR differs between Stripe and Salesforce by 2–5%, even though the contract value is correct in both systems.

Common causes:

How Eru handles this: Eru normalises all multi-currency values using a single, consistent exchange rate source (configurable: ECB daily rates, Stripe rates, or your own rate table). Both the Stripe and Salesforce values are converted using the same rate and the same reference date, eliminating rate-source and rate-timing discrepancies. The original values in each system's native currency are preserved alongside the normalised values so you can trace any difference back to the exchange rate.

Delayed webhook processing from Stripe

Symptom: Revenue metrics in Eru lag behind what you see directly in the Stripe dashboard. A subscription change that happened 10 minutes ago isn't reflected yet.

Common causes:

How Eru handles this: Eru monitors webhook arrival rate and latency continuously. If no events arrive from Stripe within 5 minutes (configurable), Eru initiates a gap detection check: it queries the Stripe API directly for recent events and compares against received webhooks. Any missed events are backfilled automatically from the API. This dual-path approach — webhooks for real-time speed, API polling for reliability — ensures no events are lost even during Stripe delivery issues. The pipeline health dashboard shows webhook arrival latency, processing queue depth, and any backfill operations in progress.

Source-of-truth hierarchy

When systems disagree, Eru needs to know which system wins. Rather than applying a single global rule, Eru uses a configurable source-of-truth hierarchy that varies by data type:

Data Type Primary Source (Default) Rationale
Billing / MRR / ARR Stripe (or billing system) Billing system records what was actually charged. CRM records intent, not reality.
Account ownership / CSM Salesforce (or CRM) CRM is the system of record for account relationships and team assignments.
Product usage / engagement Analytics platform Analytics captures actual user behaviour, not sales or billing proxies.
Support ticket status Intercom / Zendesk Support platform has real-time ticket state; CRM syncs are often delayed.
Contract terms / renewal dates Salesforce (or CRM) Contract terms are negotiated and recorded in the CRM during the sales process.
Customer health score Eru (composite) No single system has the full picture. Eru computes health from all sources.

These defaults are configurable per workspace. If your organisation uses Salesforce as the billing source of truth (e.g., CPQ-driven billing), you can override the hierarchy. Eru surfaces conflicts regardless of the hierarchy — the hierarchy determines which value is treated as authoritative for downstream calculations, not which conflicts are hidden.

What this means for your existing analytics setup

Eru is designed to integrate with your existing stack without disrupting it. A few important points for teams evaluating pipeline reliability:

Related resources

See pipeline reliability in action

Eru runs continuous reconciliation checks across all your systems. Data conflicts surface in Slack with root causes and suggested resolutions before they become board-level problems.