Track lead-to-MQL conversion rate by source and report to Slack using Make
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:writescope) - 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
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
}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
- Click Run once to execute the scenario
- Inspect each module's output — verify lead counts match your HubSpot dashboard
- 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_SEARCHsources, drill intohs_analytics_source_data_1to 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.