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
Why Zapier?
Zapier is the right choice if your team is already paying for a Professional plan and wants a managed, no-infrastructure solution. The trade-off is cost and complexity — this leaderboard requires Webhooks by Zapier (to call HubSpot's Search API directly, since native actions don't support engagement searches), Code by Zapier (for ranking logic), and multiple steps. At ~7 tasks per run and 20 weekday runs/month, you'll use about 140 tasks/month.
If you're not already on Zapier, n8n (free self-hosted) or the Code approach ($0) are more economical. But if Zapier is your team's standard automation platform and you value managed infrastructure over cost, this works well.
How it works
- Schedule by Zapier fires every weekday morning at your configured time
- Webhooks by Zapier makes 4 direct HTTP calls: one for the owner roster and three for yesterday's calls, emails, and meetings via HubSpot's Search API
- Code by Zapier (JavaScript) parses all responses, counts per-rep activities, ranks by total, and assigns medal emojis
- Slack action posts the formatted leaderboard to your channel
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
Troubleshooting
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.
Common questions
Why does this Zap need the Professional plan?
The Free plan doesn't support Webhooks by Zapier, Code by Zapier, or multi-step Zaps. All three are required for this leaderboard — Webhooks to call HubSpot's Search API directly, Code to rank reps, and multiple steps to chain the logic. The Professional plan starts at $29.99/mo (billed annually) with 750 tasks/month.
How many Zapier tasks does this use per month?
Each daily run uses ~7 tasks (1 schedule + 1 owners webhook + 3 search webhooks + 1 code + 1 Slack). At 20 weekday runs/month, that's ~140 tasks/month. The Professional plan's 750-task limit handles this comfortably with room for other Zaps.
Can I add pagination for teams with 100+ daily activities?
Yes, but it's complex. You'd need additional Webhook steps with after cursors for each activity type, plus looping logic. At that volume, the Code + Cron approach is simpler and free.
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.
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.