Automate a weekly pipeline report with HubSpot and Slack using Zapier
Prerequisites
- Zapier account on the Professional plan or higher (required for multi-step Zaps, Code by Zapier, and Webhooks by Zapier)
- HubSpot account connected to Zapier via OAuth
- Slack workspace connected to Zapier
Overview
Zapier's built-in HubSpot integration doesn't have a "get all deals" action — it's designed around individual deal triggers. For a weekly aggregated report, you'll use Schedule by Zapier to trigger weekly, Webhooks by Zapier to call the HubSpot API directly, Code by Zapier to process the data, and Slack to deliver the report.
Zapier's native HubSpot actions work on individual records (e.g., "New Deal" trigger). For a report that aggregates all pipeline deals, you need to call the HubSpot Search API directly using Webhooks by Zapier, which lets you make custom HTTP requests.
Step 1: Add a Schedule trigger
Create a new Zap. Choose Schedule by Zapier as the trigger:
- Trigger event: Every Week
- Day of the week: Monday
- Time of day: 8:00am
- Timezone: Select your team's timezone
Step 2: Fetch deals via Webhooks by Zapier
Add a Webhooks by Zapier action step:
- Action event: Custom Request
- Method: POST
- URL:
https://api.hubapi.com/crm/v3/objects/deals/search - Headers:
Authorization:Bearer YOUR_HUBSPOT_PRIVATE_APP_TOKENContent-Type:application/json
- Data (raw JSON):
{
"filterGroups": [
{
"filters": [
{
"propertyName": "pipeline",
"operator": "EQ",
"value": "default"
}
]
}
],
"properties": [
"dealname", "amount", "dealstage",
"closedate", "hubspot_owner_id"
],
"limit": 100
}The HubSpot Search API returns a max of 100 results per request. Zapier doesn't natively support looping through paginated API responses in a single Webhook step. If you have more than 100 active deals, you'll need to add additional Webhook steps with the after cursor, or use Code by Zapier to handle pagination.
Step 3: Process data with Code by Zapier
Add a Code by Zapier step (JavaScript) to parse the API response and calculate metrics:
- Input Data: Map the response body from the Webhook step to a variable called
rawData
const data = JSON.parse(inputData.rawData);
const deals = data.results || [];
let totalValue = 0;
const byStage = {};
for (const deal of deals) {
const amount = parseFloat(deal.properties.amount || '0');
totalValue += amount;
const stage = deal.properties.dealstage || 'unknown';
byStage[stage] = (byStage[stage] || 0) + 1;
}
const stageBreakdown = Object.entries(byStage)
.map(([stage, count]) => `• ${stage}: ${count} deals`)
.join('\n');
return {
totalValue: totalValue.toLocaleString('en-US', {
style: 'currency',
currency: 'USD',
maximumFractionDigits: 0
}),
dealCount: deals.length,
stageBreakdown,
reportDate: new Date().toLocaleDateString('en-US', {
weekday: 'long',
month: 'long',
day: 'numeric',
year: 'numeric'
})
};Code steps have a 30-second timeout (Pro plan and above; 10 seconds on Starter). The script runs in a sandboxed environment with no external HTTP requests — you can't call additional APIs from within Code by Zapier. All data must be passed in via Input Data.
Step 4: Send to Slack
Add a Slack action step:
- Action event: Send Channel Message
- Channel:
#sales-reports - Message Text:
📊 *Weekly Pipeline Report*
*Total Pipeline:* {{totalValue}}
*Active Deals:* {{dealCount}}
*Deals by Stage:*
{{stageBreakdown}}
_Report generated {{reportDate}}_If you need richer formatting with Block Kit, use a Webhooks by Zapier step instead to POST directly to the Slack API:
- Method: POST
- URL:
https://slack.com/api/chat.postMessage - Headers:
Authorization: Bearer xoxb-your-bot-token - Data: Block Kit JSON with sections, fields, and dividers
Zapier's native "Send Channel Message" action supports basic mrkdwn formatting but not full Block Kit JSON. For formatted reports with columns and dividers, use the Webhooks approach to call chat.postMessage directly.
Step 5: Test and publish
- Click Test on each step to verify the data flows correctly
- Review the Slack message output — check that metrics and formatting look right
- Turn the Zap On
Cost and task usage
- Professional plan: $29.99/mo (billed annually) with 750 tasks/month
- This Zap uses ~4 tasks/month (one per Monday): 1 schedule trigger + 1 webhook + 1 code + 1 Slack = 4 steps, but Zapier counts each Zap run as tasks based on successful actions. Formatter and Filter steps no longer count toward task usage.
- Code by Zapier rate limit: 225 calls per 10 seconds on Pro (increased from 75 in 2025)
Limitations
- No native pagination: Zapier can't loop through HubSpot's paginated API results without Code or additional Webhook steps. For 100+ deals, this gets cumbersome.
- No persistent state: You can't store last week's metrics for week-over-week comparison without an external store (Google Sheets, Zapier Tables).
- Code step sandboxing: Code by Zapier can't make HTTP requests — all data must be passed in. Complex transformations may hit the 30-second timeout.
- Stage names: The HubSpot API returns stage IDs (like
closedwon), not labels. You'd need an additional Webhook step to fetch pipeline stages, or hardcode the mapping in your Code step.
Next steps
- Add pipeline stage resolution — add a second Webhook step before the Code step to call
GET /crm/v3/pipelines/dealsand map stage IDs to labels - Store historical data — add a Google Sheets or Zapier Tables step to log metrics each week
- Add filters — use Zapier's Filter step to only send the report if there are new deals (skip weeks with no changes)
Need help implementing this?
We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.