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)

Why Make?

Make is a good fit if your team already uses it for other automations. The visual scenario builder makes the two-query pattern (all leads vs. MQLs) easy to understand. The Free plan handles the HTTP modules and scheduling, but the Code module needed for percentage calculations requires the Core plan at $29/mo. At ~20 credits per month for 4 weekly runs, the credit cost is negligible.

How it works

  • Scheduler triggers weekly on Monday morning
  • Set variable module computes 7-day lookback timestamps in milliseconds
  • Two HTTP modules search HubSpot — one for all leads created in the period, one filtered to MQLs
  • Code module (Core plan) groups by source, calculates conversion percentages, and formats the breakdown
  • Slack module posts the formatted report

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

Troubleshooting

Common questions

Does this work on the Make Free plan?

Partially. The Free plan (1,000 credits/month) supports the HTTP modules and Slack module, but not the Code module needed for percentage calculations. Without the Code module, you'd need a complex combination of Iterator + Array Aggregator modules to group by source and compute rates. The Core plan ($29/mo) is recommended.

How many Make credits does this use per month?

Each run uses ~5 credits (1 trigger + 1 variable + 2 searches + 1 code + 1 Slack). At 4 weekly runs/month, that's ~20 credits — well within the Free plan's 1,000 credits or the Core plan's 10,000 credits.

Can I add campaign-level breakdown within a source?

Yes. Include hs_analytics_source_data_1 in the search properties. This gives you UTM campaign data for sources like PAID_SEARCH. Add a second grouping level in the Code module to break down high-volume sources by campaign.

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

Looking to scale your AI operations?

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