8 component deliveries, 339 customer accounts auto-synced from Stripe to Attio with zero data loss
A 400+ customer vertical SaaS platform needed Attio to become the single source of revenue truth across product, billing, support, and sales. Over 12 weeks we shipped 8 components — duplicate cleanup, real-time auto-dedup, full Stripe revenue sync, sales engagement tracking, lifecycle orchestration across Lemlist + Intercom, and the entire reporting layer.
8
Component deliverables shipped
339
Stripe customers backfilled into Attio
10+
Live n8n workflows in production
9,000+
Person records under real-time dedup
The situation
A 400+ customer vertical SaaS platform operating with 10K+ records in Attio across companies, contacts, and deals. Their core problems were the structural ones every scaling B2B SaaS hits between Series A and Series B:
- Duplicate contacts at scale. A person could exist 3 times — once from a signup, once from a sales import, once from a support ticket. Reporting was unreliable because none of them knew about each other.
- Stripe and Attio knew different things about the same customer. The MRR, plan, churn state, and lifetime value lived in Stripe; everything else lived in Attio; nobody had the merged view.
- Sales engagement was happening in Lemlist + WhatsApp but the activity wasn’t being written back to Attio, so the sales pipeline view was missing the actual outbound state of every account.
- No clear lifecycle orchestration. A contact could be in a Lemlist sequence and an Intercom support conversation and a sales pipeline stage all at once, with no clean ownership rule.
What we built
8 component deliveries across a 12-week retainer.
Foundation (Weeks 1–2)
Duplicate cleanup — Python tooling: detect (Levenshtein fuzzy match + completeness scoring), CSV export for review, batch merge with the highest-quality master record kept. One-time pass across the entire 9,000+ contact database.
Automatic dedup — A real-time n8n workflow listens to Attio’s record.created webhook, fuzzy-matches every new person against the existing 9,000+ database (≥90% Levenshtein, 3-char prefix filter for performance), and merges immediately if a duplicate is found. Every new record self-cleans the moment it’s written. Zero manual review needed.
Revenue layer (Weeks 3–6)
Stripe integration — 4 n8n workflows + a one-time backfill:
- Subscription lifecycle: Stripe
customer.subscription.created/updated/deleted→ Attio Companysubscription_status,current_plan,MRR,stripe_customer_id - Payment sync:
payment_intent.succeeded→ resolves the customer ID → Attio Companytotal_lifetime_value,last_payment_date - Trial activation: trial subscription created → corresponding Deal moved to “In Trial” stage
- Churn detection: subscription canceled → Client record
client_statusflipped to “Churned”
Stripe event → Attio Company field updated
─────────────────────────────────────────────────────────────
subscription.created → subscription_status, current_plan, MRR
subscription.updated → subscription_status, current_plan, MRR
subscription.deleted → subscription_status, Client.client_status
payment_intent.succeeded → total_lifetime_value, last_payment_date
subscription (status=trial) → Deal.stage = "In Trial"
Backfill: 372 Stripe subscriptions processed → 339 Attio Companies updated, 33 skipped (cleanly, missing identifier), 0 errors.
The trickiest part: the business identifier (gymId) lives on Stripe subscription metadata, not on customer metadata. Payment events don’t carry it directly — they need a resolution chain.
payment_intent.succeeded
└─ extract customer_id
└─ stripe.list_subscriptions(customer_id)
└─ first sub.metadata.gymId
└─ patch Attio Company WHERE gymid = {gymId}
Reporting & dashboards — Built natively inside Attio’s reporting tool. No external BI, no extra subscriptions, no third-party data flow. The whole pipeline + revenue picture lives where the team already works.
Engagement layer (Weeks 5–7)
Sales engagement sync — 2 n8n workflows write Lemlist + WhatsApp activity back to the Attio Company record (last_outreach_date, last_outreach_channel, current_sequence, sequence_ended, last_whatsapp_date). System of engagement (Lemlist, WhatsApp) stays separate from system of record (Attio); only what matters for pipeline visibility crosses the line.
Lifecycle orchestration — 3 n8n workflows enforce “one contact, one owner, one inbox at a time”:
- Lifecycle stage router (18-node workflow): when an Attio lifecycle stage changes, route to the right action — enroll/remove from Lemlist sequences, assign/close Intercom conversations, update the system-owner field.
- Deal owner sync: when a deal owner changes, reassign open Intercom conversations to match.
- Mutual exclusion guard: scheduled workflow runs every 6h, alerts Slack if any contact ends up in a forbidden state (e.g. active in both Lemlist and Intercom simultaneously).
Attio lifecycle change → n8n router
├─ Demo Booked → remove Lemlist, alert sales in Slack
├─ Customer → remove Lemlist, assign CSM in Intercom
├─ Churned → close Intercom, optional win-back enroll
└─ Lost → archive across all systems
every 6h: mutual_exclusion_guard()
for contact in all:
if active_in(Lemlist) AND active_in(Intercom):
slack.alert("forbidden state", contact)
Support layer
Intercom ticketing — Custom Tickets object in Attio + an 8-node n8n sync workflow. Tickets created, closed, reopened, snoozed, replied — every state change writes back to Attio, linked to the right Company. Skeleton built and ready to activate when Intercom production goes live.
Outcome
- 8 component deliverables shipped across 12 weeks, on time, every week.
- 339 customer Companies in Attio now carry live Stripe revenue data —
MRR,subscription_status,current_plan,LTV,last_payment_date— kept current by webhook on every Stripe event. - 9,000+ person database under real-time duplicate protection.
- 10+ n8n workflows in production orchestrating Stripe, Lemlist, WhatsApp, Intercom, and Attio together.
- Native Attio reports replacing what would have been a BigQuery + Looker setup at 5x the cost.
Why this matters for vertical SaaS at scale
Once a SaaS hits a few hundred customers, the cost of not having clean Stripe ↔ CRM sync isn’t measured in hours of manual work — it’s measured in deals you don’t follow up on, churns you don’t see coming, and reports you can’t trust enough to act on. The fix is not a 6-figure RevOps stack with Salesforce + DataLoader + Tableau. It’s Attio + a handful of well-designed n8n workflows + a real-time dedup loop. Built right, it costs an order of magnitude less and the ownership stays with you, not a Salesforce admin contractor.
Stack: Stripe · Intercom · Lemlist · WhatsApp · n8n