Post a daily Slack leaderboard of rep activity from HubSpot using Zapier
Prerequisites
- Zapier account on the Professional plan or higher (required for multi-step Zaps, Code by Zapier, and Webhooks by Zapier)
- HubSpot private app token with engagement scopes
- Slack workspace connected to Zapier
Overview
Zapier's built-in HubSpot integration doesn't have a "list engagements" action. To build a leaderboard, you'll use Schedule by Zapier to trigger daily, Webhooks by Zapier to call HubSpot's Engagements API directly, Code by Zapier to rank reps, and Slack to post the result.
Zapier's native HubSpot actions work on individual records (triggers like "New Engagement"). For a daily aggregated leaderboard, you need to search all of yesterday's activities — which requires calling the HubSpot Search API directly via Webhooks by Zapier.
Step 1: Add a Schedule trigger
Create a new Zap. Choose Schedule by Zapier:
- Trigger event: Every Day
- Time of day: 8:00am
- Day of the week: Choose Monday through Friday only (skip weekends)
- Timezone: Select your team's timezone
Step 2: Fetch rep owners via Webhooks
Add a Webhooks by Zapier action:
- Action event: Custom Request
- Method: GET
- URL:
https://api.hubapi.com/crm/v3/owners?limit=100 - Headers:
Authorization: Bearer YOUR_HUBSPOT_PRIVATE_APP_TOKEN
This returns your rep roster with IDs and names for mapping later.
Step 3: Fetch yesterday's calls
Add another Webhooks by Zapier action:
- Method: POST
- URL:
https://api.hubapi.com/crm/v3/objects/calls/search - Headers:
Authorization:Bearer YOUR_HUBSPOT_PRIVATE_APP_TOKENContent-Type:application/json
- Data (raw JSON):
{
"filterGroups": [
{
"filters": [
{
"propertyName": "hs_timestamp",
"operator": "GTE",
"value": "YESTERDAY_START_MS"
},
{
"propertyName": "hs_timestamp",
"operator": "LT",
"value": "TODAY_START_MS"
}
]
}
],
"properties": ["hs_timestamp", "hubspot_owner_id"],
"limit": 100
}Replace YESTERDAY_START_MS and TODAY_START_MS with Zapier formatter steps or compute them in the Code step.
Step 4: Fetch emails and meetings
Repeat Step 3 twice with different URLs:
- Emails:
https://api.hubapi.com/crm/v3/objects/emails/search - Meetings:
https://api.hubapi.com/crm/v3/objects/meetings/search
Use the same filter body for both.
Each Webhook call is a separate Zap step and counts toward your task usage. This Zap uses 6+ steps per run (schedule + owners + 3 searches + code + Slack). On the Professional plan at $29.99/mo, you get 750 tasks/month. Daily weekday runs use ~140 tasks/month.
Step 5: Rank reps with Code by Zapier
Add a Code by Zapier step (JavaScript). Map the raw responses from the three Webhook steps into input variables:
ownersRaw-> response body from the owners WebhookcallsRaw-> response body from the calls WebhookemailsRaw-> response body from the emails WebhookmeetingsRaw-> response body from the meetings Webhook
const owners = JSON.parse(inputData.ownersRaw).results || [];
const calls = JSON.parse(inputData.callsRaw).results || [];
const emails = JSON.parse(inputData.emailsRaw).results || [];
const meetings = JSON.parse(inputData.meetingsRaw).results || [];
const ownerMap = {};
for (const o of owners) {
ownerMap[o.id] = `${o.firstName || ''} ${o.lastName || ''}`.trim() || o.email;
}
const reps = {};
function count(items, type) {
for (const item of items) {
const oid = item.properties.hubspot_owner_id;
if (!oid) continue;
if (!reps[oid]) reps[oid] = { calls: 0, emails: 0, meetings: 0, total: 0 };
reps[oid][type]++;
reps[oid].total++;
}
}
count(calls, 'calls');
count(emails, 'emails');
count(meetings, 'meetings');
const medals = ['\u{1F947}', '\u{1F948}', '\u{1F949}'];
const ranked = Object.entries(reps)
.map(([id, c]) => ({ name: ownerMap[id] || id, ...c }))
.sort((a, b) => b.total - a.total);
const lines = ranked.map((r, i) => {
const medal = medals[i] || `${i + 1}.`;
return `${medal} *${r.name}* — ${r.total} activities (${r.calls}C ${r.emails}E ${r.meetings}M)`;
});
const yesterday = new Date(Date.now() - 86400000);
return {
leaderboard: lines.join('\n'),
reportDate: yesterday.toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' })
};Step 6: Send to Slack
Add a Slack action:
- Action event: Send Channel Message
- Channel:
#sales-activity - Message Text:
:trophy: *Rep Activity Leaderboard*
Activity for {{reportDate}}
{{leaderboard}}
_C = Calls | E = Emails | M = Meetings_Step 7: Test and publish
- Click Test on each step to verify data flows correctly
- Review the Slack message — check that rep names resolve and counts look right
- Turn the Zap On
Cost and task usage
- Professional plan: $29.99/mo (billed annually) with 750 tasks/month
- This Zap uses ~7 tasks per run: 1 schedule + 1 owners + 3 searches + 1 code + 1 Slack = 7 steps. At 20 weekday runs/month, that's ~140 tasks/month.
- If you have more than 100 activities of any type per day, you'll need additional Webhook steps for pagination, increasing task usage.
Limitations
- No native pagination: If your team logs more than 100 calls, emails, or meetings per day, you'll only capture the first 100 of each type. You'd need additional Webhook steps with
aftercursors. - Code step sandboxing: Code by Zapier can't make HTTP requests — all data must be passed in via Input Data.
- Timestamp computation: Computing yesterday's midnight in milliseconds requires either a Formatter step or inline calculation in the Code step.
Need help implementing this?
We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.