Automate a weekly Salesforce pipeline report using Flow Builder
Prerequisites
- Salesforce org with Flow Builder access (available in Enterprise, Performance, Unlimited, and Developer editions)
- A Slack workspace with an Incoming Webhook URL configured
- Admin or Customize Application permission in Salesforce
Why Salesforce Flow?
Flow Builder keeps everything inside Salesforce — no middleware, no external subscriptions, no OAuth credentials to manage. The Scheduled Flow runs reliably on Salesforce's infrastructure with access to all your Opportunity data. The trade-off is verbose aggregation logic: Flow Builder has no dictionary type, so grouping by stage requires one counter variable per stage and Decision elements inside a Loop. For orgs with standard pipeline stages that rarely change, this is manageable. For dynamic reporting needs, the n8n or Claude Code approaches are more flexible.
Overview
Salesforce Flow Builder lets you create a Scheduled Flow that runs every Monday, queries open Opportunities via a Get Records element, aggregates metrics using Collection variables, and sends the result to Slack via an HTTP Callout action. Everything stays inside Salesforce — no external tools needed.
Step 1: Create a Scheduled Flow
In Salesforce Setup, search for Flows and click New Flow. Select Schedule-Triggered Flow.
Set the schedule:
- Start date: Next Monday
- Frequency: Weekly
- Day: Monday
- Time: 8:00 AM (your team's timezone)
Step 2: Add a Get Records element for open Opportunities
Add a Get Records element to query open Opportunities:
- Object: Opportunity
- Conditions:
IsClosedequalsfalseCloseDateequals or afterTHIS_FISCAL_QUARTER(use a formula resource for dynamic dates)
- Fields:
Id,Name,Amount,StageName,CloseDate,OwnerId - Store: All records → in a Record Collection variable (
varOpportunities) - Sort: Amount, Descending
Step 3: Loop and aggregate metrics
Add a Loop element to iterate through varOpportunities. Inside the loop, use Assignment elements to:
- Increment total pipeline value — add each Opportunity's
Amountto a Number variable (varTotalPipeline) - Count deals per stage — use a Collection variable (Text Collection) to track stage names and counts. For simplicity, use separate counter variables for each stage (e.g.,
varProspectingCount,varNegotiationCount) - Count total deals — increment a counter variable (
varDealCount)
Flow Builder doesn't have native map/dictionary types. The easiest approach is to create one Number variable per pipeline stage (matching your actual stages) and increment the right one inside the Loop using Decision elements.
Step 4: Build the report text
After the Loop, add an Assignment element to compose a Text variable (varReportBody) with your metrics. Use a Text Template resource for cleaner formatting:
Weekly Pipeline Report
Total Pipeline: ${varTotalPipeline}
Active Deals: {!varDealCount}
Deals by Stage:
• Prospecting: {!varProspectingCount}
• Qualification: {!varQualificationCount}
• Proposal: {!varProposalCount}
• Negotiation: {!varNegotiationCount}
• Closed Won This Week: {!varClosedWonCount}
Average Deal Size: {!varAvgDealSize}Step 5: Send to Slack via HTTP Callout
Add an Action element using an HTTP Callout (available in Spring '23+). Configure it as an External Service or use an Apex invocable action:
HTTP Callout setup:
- Method: POST
- URL: Your Slack Incoming Webhook URL
- Content-Type:
application/json - Body:
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Weekly Pipeline Report"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Total Pipeline*\n${varTotalPipeline}"
},
{
"type": "mrkdwn",
"text": "*Active Deals*\n${varDealCount}"
}
]
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Deals by Stage*\n${varStageBreakdown}"
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Report generated ${varReportDate}"
}
]
}
]
}If HTTP Callout isn't available in your org, create a simple Apex class with an @InvocableMethod annotation that accepts the report text and POSTs to the Slack webhook. Call it from the Flow as an Action element.
Step 6: Handle the Reports API alternative
If you already have a Salesforce report that shows your pipeline data, you can skip the Get Records + Loop approach entirely. Instead, use an HTTP Callout to execute the saved report:
GET /services/data/v64.0/analytics/reports/{reportId}Parse the response in a Code-based Flow element or Apex action, then forward the results to Slack. This is especially useful for complex reports with cross-filters or custom report types that are hard to replicate in Flow.
Scheduled Flows can process up to 250,000 records per batch, but each Get Records element returns a maximum of 2,000 records per transaction. If your pipeline has more than 2,000 open Opportunities, use the Reports API approach or an Apex action with a SOQL query that uses GROUP BY StageName to aggregate server-side.
Step 7: Test and activate
- Click Debug in Flow Builder and run the Flow manually
- Check each element's output — verify the Opportunity count and amounts are correct
- Confirm the Slack message arrives with proper formatting
- Click Activate to enable the weekly schedule
Troubleshooting
Cost
Salesforce Flows are included with your Salesforce license at no additional cost. Slack Incoming Webhooks are free.
Common questions
Can I use a saved Salesforce report instead of Get Records?
Yes. Use the Reports API (GET /services/data/v64.0/analytics/reports/{reportId}) via an HTTP Callout. This is simpler when your report has complex filters or cross-object joins. Parse the response JSON in an Apex invocable action and pass the formatted text to Slack.
What if I have more than 2,000 open Opportunities?
The Get Records element returns a maximum of 2,000 records per transaction. For larger pipelines, use an Apex invocable action with a SOQL GROUP BY StageName query that aggregates server-side, or use the Reports API approach.
How do I add a per-rep breakdown?
Add OwnerId to the Get Records fields and include a second Loop that groups by owner. You'll need one counter variable per rep (or a limited roster), which gets verbose. For dynamic per-rep reporting, the n8n or Claude Code approaches handle this more naturally.
Limitations
- Flow Builder's lack of dictionary types makes dynamic stage aggregation verbose — you need one variable per stage
- HTTP Callout formatting is limited compared to building JSON in code
- For complex reports with calculated fields, the Reports API approach is more practical
- Scheduled Flows run in system context — they query all Opportunities regardless of sharing rules (which is usually what you want for a team report)
Next steps
Once the basic report is running, consider:
- Owner breakdown — add a second Loop that groups by
OwnerIdand includes a per-rep section in the Slack message - Week-over-week deltas — store last week's totals in a Custom Setting or Custom Metadata Type and calculate the change
- Stale deal flag — add a Decision element inside the Loop to flag Opportunities with no activity in 14+ days
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.