Fundable Revenue & ICP Pipeline
Keeps HubSpot revenue equal to what Stripe actually bills, and tags contacts by ICP.
Pythonn8nStripe APIHubSpot CRM APIClerkHeyReachExaGeminiApollo
Problem
Fundable's revenue lived in Stripe, its pipeline in HubSpot, signups in Clerk and outreach in HeyReach, and none of them agreed. Deal amounts in the CRM drifted from what Stripe actually collected once discounts, comps and paused subscriptions were counted. The founder had no revenue board he could trust and no way to segment raw signups by ideal customer profile before outreach.
What I built
A Stripe → HubSpot revenue sync in two parts: a one-shot backfill that reprices every deal, and a Stripe-webhook n8n workflow that recomputes on every subscription create, update or delete and writes the result back to HubSpot. Each amount is billed MRR taken from a Stripe invoice preview of the next charge rather than list price, so comps, credits and expired coupons are resolved by Stripe instead of guessed at. Around it sits a contact-unification layer: Clerk signups and HeyReach leads are upserted into HubSpot and website visitors tagged, each with a source label; a reconcile script diffs Stripe against HubSpot and reports orphan subscriptions and stale deals before writing; and a Gemini classifier, with Exa company research for contacts that have no job title, stamps contacts with one of 18 ICP segments.
Highlights
- MRR is what the card gets charged, not what the plan says: one Stripe invoice-preview call per subscription replaces coupon-reconstruction logic, and edge cases such as paused, canceled and comped subscriptions resolve by a fixed rule in code rather than by hand.
- Deterministic matching: deals are stamped with Stripe subscription and customer IDs, reconciliation claims deals 1:1 so a customer with two subscriptions cannot double-claim one deal, and email is only a fallback.
- Sync, reconcile and classifier scripts all default to dry run. The reconcile prints projected totals, orphan subscriptions to be created and stale deals to be zeroed before a single CRM write; live runs chunk batch calls at HubSpot's 100-input cap and pace single-record writes.
- Internal and test accounts are excluded from revenue in code, with the same rule applied identically in the Python scripts and the n8n Code nodes, so no number depends on someone remembering the list.
- The classifier never invents a category: temperature 0, JSON mode, a fixed 18-value map with anything unrecognized collapsing to the non-ICP bucket, and contacts on personal-email domains are skipped rather than guessed. On the lead import, a codepoint-level email sanitizer (control, zero-width, line-separator and BOM characters) was added after one invisible right-to-left mark rejected an entire 100-record batch.
Numbers
- 5 n8n workflows confirmed working in production (2026-07-08)
- 18-segment ICP taxonomy (17 named segments plus a non-ICP bucket)
- 22 Python scripts and 2 n8n workflow exports