How Eru handles data conflicts and pipeline reliability across CRM, billing, and analytics systems
Three systems, three numbers, one answer. How Eru's data layer normalises cross-system metrics, resolves conflicts when CRM and billing disagree, and keeps your pipelines reliable without engineering effort.
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, SalesforceAccount ID, and analyticsorganization_idto 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:
- Attempt 1: Immediate retry
- Attempt 2: 5-second delay
- Attempt 3: 15-second delay
- Attempt 4: 45-second delay
- Attempts 5–10: Increasing delays up to 5-minute maximum
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:
- No new requests are sent to the failing system
- Incoming events for that system are queued (not dropped)
- A pipeline health alert is sent to your configured Slack channel or email
- Metrics that depend on the failing system are marked with a freshness warning
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:
- A single customer has multiple Stripe subscriptions that map to the same Salesforce account, and both are being counted
- A Stripe subscription was cancelled and recreated (common during plan migrations), creating two subscription records for the same revenue
- An account was merged in Salesforce but the old and new account IDs both still map to active Stripe subscriptions
- Webhook retries processed the same subscription creation event multiple times
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:
- Stripe converts EUR to USD at the payment processing time exchange rate. Salesforce locked the rate when the opportunity was created, potentially months earlier.
- One system uses the mid-market rate while the other uses the rate from its payment processor, which includes a spread.
- Multi-currency is enabled in Salesforce but the corporate currency rate table hasn't been updated since last quarter.
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:
- Stripe webhook delivery is delayed due to high event volume or Stripe infrastructure issues (rare but occurs during incident events)
- Your Eru webhook endpoint experienced a transient failure, causing Stripe to queue the event for retry (Stripe retries up to 3 days)
- The event was received but is in the retry queue due to a downstream processing error
- Network issues between Stripe's infrastructure and your Eru deployment
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:
- Read-only access: Eru connects to all systems with read-only permissions. It never writes back to Stripe, Salesforce, or your analytics platform. Conflicts are surfaced for your team to resolve in the source systems.
- No data warehouse required: Eru doesn't depend on a centralised warehouse. If you have one (Snowflake, BigQuery, Redshift), Eru can read from it as an additional source. If you don't, Eru connects directly to your SaaS systems.
- Existing dbt models preserved: If you've built dbt models for revenue calculation or churn scoring, Eru reads from your marts as input signals rather than replacing them. Your investment in data modelling is additive, not wasted.
- No ETL to maintain: Eru manages its own ingestion pipelines. You don't build or maintain Fivetran/Airbyte connections, dbt transformations, or reverse ETL syncs for the data Eru needs. When a Stripe API version changes or Salesforce adds a new field, Eru's integration adapts without your intervention.
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.