Track lead-to-MQL conversion rate by source and report to Slack using Make

medium complexityCost: $10-29/mo

Prerequisites

Prerequisites
  • Make account (any plan — weekly scheduling works on the Free plan)
  • HubSpot connection configured in Make via OAuth
  • Slack connection configured in Make (Bot Token with chat:write scope)
  • Lifecycle stages configured in HubSpot (Lead, Marketing Qualified Lead)

Overview

Make's scenario will run two HubSpot searches — one for all leads created in the past 7 days, and one for contacts in that period that reached MQL status. A Code module (or Array Aggregator) groups contacts by source and calculates conversion rates per source.

Step 1: Create a scenario and schedule it

Create a new scenario in Make. Configure the schedule:

  • Schedule type: At regular intervals
  • Run scenario: Every week
  • Day: Monday
  • Time: 09:00
  • Timezone: Select your team's timezone

Step 2: Set date range variables

Add a Tools -> Set variable module to compute the 7-day lookback:

  • Variable 1: start_ms = {{formatDate(addDays(now; -7); "X")}}000 (Unix milliseconds)
  • Variable 2: end_ms = {{formatDate(now; "X")}}000
Make date functions

formatDate(date; "X") returns Unix seconds. Append 000 to get milliseconds for HubSpot's filter values. Alternatively, use floor((parseDate(addDays(now; -7)) - parseDate("1970-01-01T00:00:00Z")) * 1000).

Step 3: Search for all leads created in the period

Add an HTTP module (Make an API Call):

  • URL: https://api.hubapi.com/crm/v3/objects/contacts/search
  • Method: POST
  • Body type: Raw JSON
{
  "filterGroups": [
    {
      "filters": [
        {
          "propertyName": "createdate",
          "operator": "GTE",
          "value": "{{start_ms}}"
        },
        {
          "propertyName": "createdate",
          "operator": "LT",
          "value": "{{end_ms}}"
        }
      ]
    }
  ],
  "properties": ["hs_analytics_source", "lifecyclestage", "createdate"],
  "limit": 100
}
Pagination

HubSpot Search returns max 100 results per request. If you generate more than 100 leads per week, add a Repeater module that loops back with the after cursor until paging.next is absent.

Step 4: Search for MQLs from the same period

Add a second HTTP module with an additional lifecycle stage filter:

{
  "filterGroups": [
    {
      "filters": [
        {
          "propertyName": "createdate",
          "operator": "GTE",
          "value": "{{start_ms}}"
        },
        {
          "propertyName": "createdate",
          "operator": "LT",
          "value": "{{end_ms}}"
        },
        {
          "propertyName": "lifecyclestage",
          "operator": "EQ",
          "value": "marketingqualifiedlead"
        }
      ]
    }
  ],
  "properties": ["hs_analytics_source", "lifecyclestage", "createdate"],
  "limit": 100
}

Step 5: Calculate conversion rates

Add a Code module (requires Core plan) to process both responses:

const allLeads = JSON.parse(getVariable('leads_response')).results || [];
const mqls = JSON.parse(getVariable('mqls_response')).results || [];
 
const leadsBySource = {};
for (const lead of allLeads) {
  const src = lead.properties.hs_analytics_source || 'UNKNOWN';
  leadsBySource[src] = (leadsBySource[src] || 0) + 1;
}
 
const mqlsBySource = {};
for (const mql of mqls) {
  const src = mql.properties.hs_analytics_source || 'UNKNOWN';
  mqlsBySource[src] = (mqlsBySource[src] || 0) + 1;
}
 
const sources = [...new Set([...Object.keys(leadsBySource), ...Object.keys(mqlsBySource)])];
const lines = sources
  .map(s => ({
    source: s,
    leads: leadsBySource[s] || 0,
    mqls: mqlsBySource[s] || 0,
    rate: leadsBySource[s] ? ((mqlsBySource[s] || 0) / leadsBySource[s] * 100).toFixed(1) : '0.0',
  }))
  .sort((a, b) => b.leads - a.leads)
  .map(r => `• *${r.source}*: ${r.leads} leads → ${r.mqls} MQLs (${r.rate}%)`);
 
const total = allLeads.length;
const totalMQL = mqls.length;
const overall = total > 0 ? (totalMQL / total * 100).toFixed(1) : '0.0';
 
return {
  breakdown: lines.join('\n'),
  totalLeads: total,
  totalMQLs: totalMQL,
  overallRate: overall,
};

If you're on the Free plan without the Code module, you can use Iterator + Array Aggregator modules to group by source, but the logic is significantly more complex for percentage calculations.

Step 6: Send to Slack

Add a Slack module -> Create a Message:

  • Channel: #marketing-reports
  • Text:
📈 *Weekly Lead-to-MQL Conversion Report*
 
*Total Leads:* {{totalLeads}}
*Total MQLs:* {{totalMQLs}}
*Overall Conversion:* {{overallRate}}%
 
*Conversion by Source:*
{{breakdown}}
 
_Last 7 days | Generated {{formatDate(now; "dddd, MMMM D, YYYY")}}_

For Block Kit formatting, use Slack -> Make an API Call with /chat.postMessage and a JSON body containing blocks.

Step 7: Test and activate

  1. Click Run once to execute the scenario
  2. Inspect each module's output — verify lead counts match your HubSpot dashboard
  3. Toggle the scenario to Active

Cost and credits

  • Free plan: 1,000 credits/month. This scenario uses approximately 5 credits per run (1 trigger + 1 variable + 2 searches + 1 code + 1 Slack). At 4 runs/month, that's ~20 credits/month.
  • Core plan: $29/month for 10,000 credits. Required if using the Code module.

Next steps

  • Month-to-date comparison — add a second pair of searches with a 30-day lookback and include MTD totals in the report
  • Week-over-week trend — use Make's Data Store module to persist last week's rates and show change arrows
  • Campaign-level breakdown — for PAID_SEARCH sources, drill into hs_analytics_source_data_1 to show per-campaign conversion

Need help implementing this?

We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.