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