Most channel reports stop at leads. The channel producing the most leads is frequently not the one producing the most revenue, and that gap is the entire point of this analysis.
Steps
-
Understand the CRM shape before querying it. Call
hubspot.list_propertiesfor contacts and deals to find the actual source field — it may behs_analytics_source, a custom property, or something team-specific. Callhubspot.list_pipelinesfor deals to learn which stage IDs mean "closed won". Do not hardcode either; they differ per account. -
Pull deals with their source and value.
hubspot.search_dealswithfilterGroupsscoping to the period, requestingpropertiesincluding deal name, amount, close date, stage, and the source property you identified. Page through withafteruntil complete — a partial pull silently biases every number downstream. -
Pull the contacts behind those deals via
hubspot.search_contactswhen you need the original acquisition source rather than the deal-level one. The deal source is often inherited or overwritten; the contact's first-touch source is usually closer to the truth. -
Reconcile against actual payments. CRM "closed won" is a human-entered state; money received is not. Use
stripe.list_chargesorstripe.list_invoices(withstatus: "paid") over the same window and compare totals. Where they diverge, trust Stripe for revenue and report the discrepancy — it's usually a finding in its own right. -
Add top-of-funnel volume for context.
google_analytics.run_reportwithdimensions: ["sessionSourceMedium"]andmetrics: ["sessions", "conversions"]shows how much traffic each channel needed to produce those deals. -
Report. One row per source: sessions, leads, deals created, deals won, revenue, and revenue per lead. Sort by revenue. Then explicitly contrast the lead-volume ranking against the revenue ranking — where they disagree is the recommendation.
Gotchas
- Attribution windows. A deal closing this month may come from a lead generated months ago. Attributing by close date credits whatever channel was running then. Prefer joining on the contact's original source and note the lag.
- HubSpot source values are messy. Free-text and legacy values proliferate. Normalize case and obvious duplicates before grouping, and say what you merged.
- Stripe amounts are in the smallest currency unit (cents). Divide by 100 before reporting, and don't sum across currencies without converting — if multiple currencies appear, report them separately.
- Deleted or test deals frequently sit in CRM data. Sanity-check the total against a known figure before presenting it.
- Small numbers aren't signal. A channel with 3 deals and one large contract will top a revenue-per-lead table. Report deal counts alongside so the reader can see the sample size.