Google Analytics 4 + HubSpot Contact Lifecycle: Weekly Acquisition-Quality Digest to Microsoft Teams via Pipedream
Every Monday, this workflow cross-references last week's GA4 traffic sources with HubSpot contact creation and lifecycle progression — then posts a digest to Microsoft Teams showing which acquisition channels are actually producing contacts that convert.
The stack in the order it runs — data flows from the source through to where it lands.
The most expensive early-stage growth mistake I've seen: optimizing for traffic channels that look great in GA4 but produce contacts that stall at 'Marketing Qualified Lead' in HubSpot forever. GA4 tells you volume and source. HubSpot tells you what happened to those people afterward. Without a data team or a BI tool, nobody joins these two views — so the gap just costs you money quietly.
Pipedream is the right call here because both GA4's Data API and HubSpot's CRM API require OAuth flows and multi-step data joins. That's easier to write in code than to wire up in a low-code drag-and-drop tool. Pipedream's component library has pre-built GA4 and HubSpot auth — you connect accounts in the UI and write the join logic in JavaScript steps.
GA4's Data API (`runReport`) returns sessions, users, and conversions broken down by `sessionDefaultChannelGroup` (Organic, Paid, Direct, Referral, etc.) for any date range you specify. HubSpot's Contacts API filtered by `createdate` and `lifecyclestage` gives you contacts created last week and their current stage. The join is by week — you're comparing each channel's share of traffic against the share of contacts it produced and how far those contacts have progressed.
One tradeoff to be honest about: the attribution join is approximate. GA4 and HubSpot use different attribution models, and not every GA4 session becomes an identified HubSpot contact. You need UTM parameters on all campaign links and HubSpot's tracking code installed correctly for the attribution to hold. If UTM coverage is below ~60% of inbound contacts, this digest will have attribution gaps — call that out explicitly in the Teams message so nobody over-indexes. And skip this playbook if you're spending more than $10K/month on paid acquisition. At that level you need a proper data warehouse (Supabase or BigQuery) and a BI layer. This is for teams spending $500–$5K/month who want directional signal without a data hire.
The stack (4)
Web analytics most teams already run.
The Data API makes traffic a free input for weekly ops digests.
Chat + channels for Microsoft-365 shops.
If the company is on Outlook/365, Teams is where reports get read — push there, not a separate tool.
How it runs
- 1
Create the Pipedream workflow with a Monday morning CRON trigger
In Pipedream, create a new workflow. Set the trigger to Schedule → CRON expression `0 8 * * 1` (8 AM UTC every Monday). Name it `ga4-hubspot-acquisition-quality`. Connect your Google Analytics account via Pipedream's Google OAuth integration and your HubSpot account via Pipedream's HubSpot app. Both connections are UI-driven — no manual token management.
- 2
Query GA4 Data API for last week's sessions by channel
Add a Node.js code step. Use the `@googleapis/analyticsdata` npm package, which is available in Pipedream. Call `runReport` with: `dateRanges = [{startDate: 'last Monday', endDate: 'last Sunday'}]`, `dimensions = [{name: 'sessionDefaultChannelGroup'}]`, `metrics = [{name: 'sessions'}, {name: 'newUsers'}, {name: 'conversions'}]`. Your GA4 Property ID is in the GA4 console under Admin → Property Settings. Parse the response into a map: `{channel: {sessions, newUsers, conversions}}`. Watch for the channel name 'Unassigned' — if it's >20% of traffic, flag it. That means UTM coverage is poor.
- 3
Query HubSpot for contacts created last week, broken down by original source
Add a second code step. Use HubSpot's POST `/crm/v3/objects/contacts/search` endpoint with filters: `createdate BETWEEN [last Monday, last Sunday]`. Return properties: `createdate`, `hs_analytics_source`, `lifecyclestage`, `hs_lead_status`. The `hs_analytics_source` property maps to GA4 channels (ORGANIC_SEARCH, PAID_SEARCH, DIRECT_TRAFFIC, etc.). Build a map: `{source: {count, mqls, sqls, customers}}` where mqls/sqls/customers are counts of contacts at each lifecycle stage. Paginate using HubSpot's `after` cursor — each page returns 100 contacts max.
- 4
Join the two datasets and compute channel quality scores
Add a Function step to normalize channel names between GA4 and HubSpot (GA4 uses 'Organic Search', HubSpot uses 'ORGANIC_SEARCH' — build a lookup map). For each channel, compute: `contactConversionRate = contacts / sessions * 100`, `mqlRate = mqls / contacts * 100`, `customerRate = customers / contacts * 100`. Rank channels by `customerRate` descending. Flag any channel where sessions > 200 but customerRate = 0 — that's wasted acquisition spend with a specific source to investigate.
- 5
Build the weekly digest table in text format
Construct the Teams message as a markdown table — Teams supports basic markdown in channel messages. Columns: Channel | Sessions | New Contacts | MQL Rate | Customer Rate | Flag. Sort rows by Customer Rate descending. Add a summary line at the top: `Top channel this week: {channel} at {customerRate}% customer conversion rate.` Add a warning line if any channel is flagged. Cap the table at 6 channels — beyond that nobody reads it.
- 6
Post the digest to Microsoft Teams via Incoming Webhook
Add an HTTP Request step in Pipedream. POST to your Teams channel's Incoming Webhook URL. Set Content-Type to `application/json`. Use Teams' MessageCard format (or Adaptive Card v1.3 for richer formatting). The table renders best in a code block inside Teams — use triple-backtick fencing for the channel table to preserve alignment. Test the webhook URL first with a simple curl command before wiring it into Pipedream.
- 7
Add an anomaly alert for channel-level drops
After the main digest post, add conditional logic: if any channel that had >100 sessions last week shows a week-over-week drop in `contactConversionRate` of >30% (compare against last week's data stored in a Google Sheets history row), post a second Teams message tagged `@channel` in `#growth-alerts`. Compute week-over-week by reading the prior row from Google Sheets and comparing. This turns the digest from a passive report into an active signal.
- 8
Write the run's data to Google Sheets for historical trending
Add a Google Sheets step at the end: Append Row. Write: week_of, top_channel, top_channel_customer_rate, total_contacts_created, total_sessions, overall_contact_conversion_rate, flagged_channels (comma-separated). After 8 weeks of data, open this sheet in Looker Studio and build a proper trend chart in 10 minutes. Pipedream generates the data; the Sheets tab is your free warehouse for it.
Want me to build this for you instead?
Product Audit and CTO Mode run out of this same thinking. If you’re reading this thinking “I want this, but in my product” — let’s talk.
See servicesMore like this
Google Analytics 4 + HubSpot Lifecycle Stage: Weekly Acquisition-Quality Digest to Slack via Pipedream with Claude Narrative
Stop reporting traffic numbers. Report whether the traffic you paid for last week actually became pipeline — with a one-paragraph executive summary written by Claude.
Gmail Thread Aging + Stripe Invoice Overdue: Unified AR Follow-Up Digest to Slack via Zapier
Surface overdue Stripe invoices and the exact age of your last Gmail thread with that customer — every morning at 8:30, automatically — so AR follow-up stops living in someone's head.
Intercom Ticket Volume + Razorpay Failed Payments: Daily Support-Cost-per-Revenue Alert to Slack via Zapier
Catch the support cost blowout from Razorpay failed payments before your agents are already buried.