

How to Send Close Deal Alerts to Slack with Pipedream
Fires a Slack notification instantly when a Close deal changes stage or crosses a value threshold, routing the alert to the right channel with deal owner, value, and next steps.
Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.
Best for
Inside sales teams of 5-50 reps who need instant visibility into pipeline movement without keeping Close open all day.
Not ideal for
Teams that only need a daily digest — schedule a Pipedream workflow to batch-summarize instead of using webhooks.
Sync type
real-timeUse case type
notificationReal-World Example
A 12-person SaaS sales team posts to #deals-won in Slack the moment a Close opportunity moves to 'Won' and posts to #deals-at-risk when a deal over $10,000 stalls in 'Proposal' for more than 7 days. Before this workflow, the sales manager checked Close manually twice a day and regularly missed same-day wins, which meant delayed commission confirmations and no team celebration moment. Now the alert fires within 8 seconds of the stage change and includes the deal value, owner name, and a direct link back to the Close lead.
What Will This Cost?
Drag the slider to your expected monthly volume.
Each platform counts differently — Zapier: 1 task per trigger. Make: 1 operation per module per record. n8n: 1 execution per run.





Prices shown for annual billing. Based on published pricing as of April 2026.
Estimated ROI
1000
min saved/mo
$583
labor value/mo
Free
no platform cost
Based on ~2 min manual effort per operation at $35/hr fully loaded labor cost.
Implementation
Import this workflow directly into Pipedream
Copy the pre-built Pipedream blueprint and paste it straight into Pipedream. All modules, filters, and field mappings are already configured — you just need to connect your accounts.
Before You Start
Make sure you have everything ready.
Optional
Field Mapping
Map these fields between your apps.
| Field | API Name | |
|---|---|---|
| Required | ||
| Deal Name | opportunity.name | |
| Stage / Status Label | opportunity.status_label | |
| Deal Owner User ID | opportunity.user_id | |
| Deal Owner Display Name | user.display_name | |
| Lead Name | opportunity.lead_name | |
| Lead ID | opportunity.lead_id | |
4 optional fields▸ show
| Deal Value | opportunity.value |
| Close Date | opportunity.date_won |
| Updated At Timestamp | opportunity.date_updated |
| Confidence Score | opportunity.confidence |
Step-by-Step Setup
pipedream.com > Workflows > New Workflow
Create a new Pipedream workflow
Go to pipedream.com and sign in. Click 'New Workflow' in the top-right corner of the dashboard. You'll land on the workflow builder canvas with an empty trigger slot at the top. Give the workflow a name — something like 'Close Deal Stage Alert to Slack' — so it's easy to find later. Pipedream saves drafts automatically, so you won't lose work if you navigate away.
- 1Click 'New Workflow' in the top-right corner
- 2Click the workflow name field at the top and type 'Close Deal Stage Alert to Slack'
- 3Click the trigger slot labeled 'Add a trigger'
Workflow Builder > Add Trigger > Close > New Event (Instant)
Add the Close webhook trigger
Search for 'Close' in the trigger search box and select the Close CRM app. Choose the trigger type 'New Event (Instant)' — this uses Close's native webhook system rather than polling, which gives you sub-10-second alert times. Connect your Close account by clicking 'Connect Account' and entering your Close API key. Pipedream stores this as a Connected Account and reuses it across workflows.
- 1Type 'Close' in the trigger search box
- 2Click the Close CRM app tile
- 3Select 'New Event (Instant)' as the trigger type
- 4Click 'Connect Account' and paste your Close API key into the field
- 5Click 'Save' to confirm the connected account
Workflow Builder > Trigger Config > Event Object Type > opportunity
Configure the Close event subscription
In the trigger configuration, set the Event Object Type to 'opportunity' and the Event Action to 'updated'. This tells Pipedream to register a webhook in Close that fires on every opportunity (deal) update. You can also select 'created' if you want alerts for brand-new deals — but for stage-change alerts, 'updated' is the right choice. Pipedream automatically handles the webhook registration and deregistration with Close's API when you deploy or pause the workflow.
- 1Set 'Event Object Type' to 'opportunity'
- 2Set 'Event Action' to 'updated'
- 3Click 'Generate Test Event' to pull a recent opportunity update as sample data
This code goes in the Node.js code step (Step 5) that runs between the Filter and the Slack send action. It formats the Close payload into a Slack Block Kit message with an emoji header, deal details section, and a direct link to the lead. Paste it directly into the Pipedream code editor and adjust the `CLOSE_BASE_URL` and emoji map to match your pipeline stages.
JavaScript — Code Stepimport axios from 'axios';▸ Show code
import axios from 'axios';
export default defineComponent({
async run({ steps, $ }) {... expand to see full code
import axios from 'axios';
export default defineComponent({
async run({ steps, $ }) {
const event = steps.trigger.event?.data || {};
// Resolve deal value — Close may send in dollars or cents depending on account config
const rawValue = event.value ?? 0;
const formattedValue = rawValue > 0
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(rawValue)
: 'Value not set';
// Map stage labels to emojis for visual scannability
const stageEmoji = {
'Won': '🏆',
'Proposal': '📄',
'Demo Scheduled': '📅',
'Negotiation': '🤝',
'Lost': '❌',
};
const emoji = stageEmoji[event.status_label] ?? '🔔';
// Resolve owner name — prefer user_name from payload, fallback to user_id
const ownerName = steps.resolve_owner?.ownerName ?? event.user_name ?? event.user_id ?? 'Unassigned';
// Build Close deep link
const CLOSE_BASE_URL = 'https://app.close.com/lead';
const leadUrl = event.lead_id
? `${CLOSE_BASE_URL}/${event.lead_id}/`
: null;
// Construct Slack Block Kit payload
const blocks = [
{
type: 'header',
text: {
type: 'plain_text',
text: `${emoji} Deal ${event.status_label ?? 'Updated'} — ${event.name ?? 'Unnamed Deal'}`,
emoji: true,
},
},
{
type: 'section',
fields: [
{ type: 'mrkdwn', text: `*Company:*\n${event.lead_name ?? 'Unknown'}` },
{ type: 'mrkdwn', text: `*Value:*\n${formattedValue}` },
{ type: 'mrkdwn', text: `*Owner:*\n${ownerName}` },
{ type: 'mrkdwn', text: `*Stage:*\n${event.status_label ?? 'Unknown'}` },
],
},
...(leadUrl ? [{
type: 'actions',
elements: [{
type: 'button',
text: { type: 'plain_text', text: 'View in Close', emoji: true },
url: leadUrl,
action_id: 'view_in_close',
}],
}] : []),
{ type: 'divider' },
];
// Fallback text for notifications and screen readers
const text = `Deal ${event.status_label ?? 'Updated'}: ${event.name ?? 'Unnamed'} | ${formattedValue} | Owner: ${ownerName}`;
return { blocks, text };
},
});
Workflow Builder > Add Step > Flow Control > Filter
Add a filter step for stage and value conditions
Click the '+' button below the trigger to add a new step. Select 'Filter' from the built-in Pipedream helpers (it appears under 'Flow Control'). This step will stop the workflow — and prevent the Slack message — if the deal doesn't meet your criteria. You'll configure two conditions: the opportunity's status_label must match your target stage (e.g., 'Won' or 'Proposal'), and optionally the value must exceed a threshold like 10000. Pipedream's Filter step short-circuits execution cleanly with zero credits wasted on downstream steps.
- 1Click the '+' icon below the trigger step
- 2Click 'Flow Control' in the step category list
- 3Select 'Filter'
- 4Set Condition 1: select the path
trigger.event.data.status_labeland set it to equal 'Won' - 5Click 'Add Condition' if you want to add a value threshold and set
trigger.event.data.valueto greater than 10000
Workflow Builder > Add Step > Run Node.js Code
Add a Node.js code step to build the Slack message
Click '+' below the Filter step and select 'Run Node.js code'. This is where you construct a structured Slack Block Kit message using data from the Close webhook payload. Using Block Kit instead of plain text gives you a formatted card with the deal name, owner, value, stage, and a clickable link back to the lead in Close. Paste the code from the Pro Tip section below into this step — it handles value formatting, fallback text for missing fields, and constructs the full Block Kit payload.
- 1Click the '+' icon below the Filter step
- 2Select 'Run Node.js code'
- 3Delete the placeholder code in the editor
- 4Paste the Pro Tip code into the editor
- 5Click 'Test' to verify the output shows a valid Slack Block Kit object
blocks, text, and attachments — this is the formatted Slack payload ready to send.This code goes in the Node.js code step (Step 5) that runs between the Filter and the Slack send action. It formats the Close payload into a Slack Block Kit message with an emoji header, deal details section, and a direct link to the lead. Paste it directly into the Pipedream code editor and adjust the `CLOSE_BASE_URL` and emoji map to match your pipeline stages.
JavaScript — Code Stepimport axios from 'axios';▸ Show code
import axios from 'axios';
export default defineComponent({
async run({ steps, $ }) {... expand to see full code
import axios from 'axios';
export default defineComponent({
async run({ steps, $ }) {
const event = steps.trigger.event?.data || {};
// Resolve deal value — Close may send in dollars or cents depending on account config
const rawValue = event.value ?? 0;
const formattedValue = rawValue > 0
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(rawValue)
: 'Value not set';
// Map stage labels to emojis for visual scannability
const stageEmoji = {
'Won': '🏆',
'Proposal': '📄',
'Demo Scheduled': '📅',
'Negotiation': '🤝',
'Lost': '❌',
};
const emoji = stageEmoji[event.status_label] ?? '🔔';
// Resolve owner name — prefer user_name from payload, fallback to user_id
const ownerName = steps.resolve_owner?.ownerName ?? event.user_name ?? event.user_id ?? 'Unassigned';
// Build Close deep link
const CLOSE_BASE_URL = 'https://app.close.com/lead';
const leadUrl = event.lead_id
? `${CLOSE_BASE_URL}/${event.lead_id}/`
: null;
// Construct Slack Block Kit payload
const blocks = [
{
type: 'header',
text: {
type: 'plain_text',
text: `${emoji} Deal ${event.status_label ?? 'Updated'} — ${event.name ?? 'Unnamed Deal'}`,
emoji: true,
},
},
{
type: 'section',
fields: [
{ type: 'mrkdwn', text: `*Company:*\n${event.lead_name ?? 'Unknown'}` },
{ type: 'mrkdwn', text: `*Value:*\n${formattedValue}` },
{ type: 'mrkdwn', text: `*Owner:*\n${ownerName}` },
{ type: 'mrkdwn', text: `*Stage:*\n${event.status_label ?? 'Unknown'}` },
],
},
...(leadUrl ? [{
type: 'actions',
elements: [{
type: 'button',
text: { type: 'plain_text', text: 'View in Close', emoji: true },
url: leadUrl,
action_id: 'view_in_close',
}],
}] : []),
{ type: 'divider' },
];
// Fallback text for notifications and screen readers
const text = `Deal ${event.status_label ?? 'Updated'}: ${event.name ?? 'Unnamed'} | ${formattedValue} | Owner: ${ownerName}`;
return { blocks, text };
},
});
channel: {{channel}}
ts: {{ts}}
Workflow Builder > Add Step > Run Node.js Code (before message step)
Resolve the deal owner's name from Close
The Close webhook payload includes user_id for the deal owner, not a display name. You need to make a one-time lookup call to Close's Users API to resolve this to a readable name. Add another Node.js step above the message-building step and fetch https://api.close.com/api/v1/me/ or cache a user map at workflow start. Alternatively, if your Close plan returns user_name directly in the opportunity payload (many do), check the sample data first — if user_name is present and populated, you can skip this step entirely.
- 1Check the trigger sample output for a
user_namefield - 2If
user_nameis null or missing, click '+' to add a Node.js step before the message builder - 3Use the Close API key from your Connected Account to call
GET https://api.close.com/api/v1/user/{user_id}/ - 4Extract
display_namefrom the response and pass it to the next step via$.export('ownerName', response.display_name)
ownerName set to a readable string like 'Sarah Mitchell' rather than a raw ID like 'user_abc123xyz'.This code goes in the Node.js code step (Step 5) that runs between the Filter and the Slack send action. It formats the Close payload into a Slack Block Kit message with an emoji header, deal details section, and a direct link to the lead. Paste it directly into the Pipedream code editor and adjust the `CLOSE_BASE_URL` and emoji map to match your pipeline stages.
JavaScript — Code Stepimport axios from 'axios';▸ Show code
import axios from 'axios';
export default defineComponent({
async run({ steps, $ }) {... expand to see full code
import axios from 'axios';
export default defineComponent({
async run({ steps, $ }) {
const event = steps.trigger.event?.data || {};
// Resolve deal value — Close may send in dollars or cents depending on account config
const rawValue = event.value ?? 0;
const formattedValue = rawValue > 0
? new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(rawValue)
: 'Value not set';
// Map stage labels to emojis for visual scannability
const stageEmoji = {
'Won': '🏆',
'Proposal': '📄',
'Demo Scheduled': '📅',
'Negotiation': '🤝',
'Lost': '❌',
};
const emoji = stageEmoji[event.status_label] ?? '🔔';
// Resolve owner name — prefer user_name from payload, fallback to user_id
const ownerName = steps.resolve_owner?.ownerName ?? event.user_name ?? event.user_id ?? 'Unassigned';
// Build Close deep link
const CLOSE_BASE_URL = 'https://app.close.com/lead';
const leadUrl = event.lead_id
? `${CLOSE_BASE_URL}/${event.lead_id}/`
: null;
// Construct Slack Block Kit payload
const blocks = [
{
type: 'header',
text: {
type: 'plain_text',
text: `${emoji} Deal ${event.status_label ?? 'Updated'} — ${event.name ?? 'Unnamed Deal'}`,
emoji: true,
},
},
{
type: 'section',
fields: [
{ type: 'mrkdwn', text: `*Company:*\n${event.lead_name ?? 'Unknown'}` },
{ type: 'mrkdwn', text: `*Value:*\n${formattedValue}` },
{ type: 'mrkdwn', text: `*Owner:*\n${ownerName}` },
{ type: 'mrkdwn', text: `*Stage:*\n${event.status_label ?? 'Unknown'}` },
],
},
...(leadUrl ? [{
type: 'actions',
elements: [{
type: 'button',
text: { type: 'plain_text', text: 'View in Close', emoji: true },
url: leadUrl,
action_id: 'view_in_close',
}],
}] : []),
{ type: 'divider' },
];
// Fallback text for notifications and screen readers
const text = `Deal ${event.status_label ?? 'Updated'}: ${event.name ?? 'Unnamed'} | ${formattedValue} | Owner: ${ownerName}`;
return { blocks, text };
},
});
Workflow Builder > Add Step > Slack > Send Message to Channel
Connect your Slack account
Click '+' below the message-building step and search for 'Slack'. Select the Slack app and choose the action 'Send Message to Channel'. Click 'Connect Account' and authorize Pipedream's Slack app via OAuth — this opens a Slack authorization screen where you select the workspace and grant the chat:write and chat:write.public scopes. Once authorized, the connected account appears in the step and your workspace's channels become selectable from a dropdown.
- 1Click '+' below the Node.js code step
- 2Search for 'Slack' and select the Slack app
- 3Choose the action 'Send Message to Channel'
- 4Click 'Connect Account' and complete the Slack OAuth flow
- 5Select your workspace from the Slack authorization screen and click 'Allow'
Workflow Builder > Slack Step > Channel + Blocks + Text
Configure the Slack message action
In the Slack step, select the target channel from the dropdown — for example #deals-won or #sales-team. Set the 'Blocks' field to reference the Block Kit output from your Node.js code step (use the path selector to pick steps.build_message.$return_value.blocks). Set the 'Text' field to the fallback string from the same output — Slack uses this for notifications and accessibility when blocks don't render. Leave 'Username' and 'Icon' fields blank unless you want a custom bot name like 'DealBot'.
- 1Select the target channel from the 'Channel' dropdown (e.g., #deals-won)
- 2Click the 'Blocks' field and select the path
steps.build_message.$return_value.blocks - 3Click the 'Text' field and select
steps.build_message.$return_value.text - 4Optionally set 'Username' to 'DealBot' for a custom sender name
pipedream.com > Workflows > [Your Workflow] > Event History
Test the full workflow end to end
In Close, find a test deal and manually move it to the stage you configured in the Filter step. Watch the Pipedream workflow run log — a new execution should appear within 8 seconds. Click the run to inspect each step's input and output. Confirm the Filter step passed, the Node.js step produced a valid Block Kit object, and the Slack step returned a 200 OK with a message.ts timestamp. Then check your Slack channel to verify the message looks correct.
- 1In Close, open a test opportunity and change its status to your target stage
- 2Switch back to Pipedream and click the workflow's 'Event History' tab
- 3Click the new execution entry to open the run inspector
- 4Expand each step to verify input/output at every stage
- 5Confirm the Slack message appeared in the correct channel
Workflow Builder > Deploy (top-right button)
Deploy the workflow
Click the green 'Deploy' button in the top-right corner of the workflow builder. Pipedream activates the Close webhook subscription and starts listening for live events. The workflow moves from 'Development' to 'Active' status. You'll see the workflow listed in your Workflows dashboard with a green 'Active' badge. From this point, every qualifying Close opportunity update fires the workflow in real time — no manual intervention needed.
- 1Click the green 'Deploy' button in the top-right corner
- 2Confirm the deployment modal by clicking 'Deploy' again
- 3Navigate to pipedream.com > Workflows to confirm the 'Active' badge appears next to your workflow
Going live
Production Checklist
Before you turn this on for real, confirm each item.
Troubleshooting
Common errors and how to fix them.
Frequently Asked Questions
Common questions about this workflow.
Analysis
Use Pipedream for this if your team has at least one person comfortable reading JavaScript, or if you want conditional routing logic that goes beyond 'send to one channel.' Pipedream's webhook processing is genuinely faster than Zapier or Make for this use case — Close fires the event, Pipedream picks it up in under 2 seconds, no polling interval involved. The code step also lets you build a proper Block Kit message with buttons and formatted sections rather than a plain-text string. The one scenario where you'd skip Pipedream: if your entire team is non-technical and wants to maintain this themselves long-term. In that case, use Zapier — the setup is less flexible but doesn't require anyone to understand async/await.
Pipedream's free tier gives you 10,000 credits per month. Each workflow run for this use case costs roughly 3 credits (trigger + filter + code step + Slack step). That means 3,333 qualifying deal updates per month before you hit the ceiling. Most inside sales teams of under 20 reps won't break 500 qualifying events/month, so the free tier holds comfortably. If you do hit the limit, Pipedream's Basic plan is $19/month for 100,000 credits — that covers about 33,000 runs/month, which is more than enough for any team short of a full SDR call center. Zapier's equivalent plan for this volume runs $49-$73/month. Make's free tier handles this workflow for free up to 1,000 operations/month, which is lower than Pipedream's ceiling for this specific run structure.
Zapier's advantage here is a no-code Slack formatter with named variables — non-technical teammates can edit the message template without touching code. Make's advantage is its visual router module, which lets you branch to different Slack channels per stage without writing conditionals. n8n's advantage is self-hosting: if your Close data is sensitive and you don't want it passing through third-party servers, n8n on your own infrastructure keeps it internal. Power Automate has no meaningful advantage for this specific workflow — its Slack connector is limited and Close has no native Power Automate action. Pipedream is still the right call here because the Block Kit formatting and the user ID resolution lookup both need real code, and Pipedream is the only platform where that code runs natively in the same workflow without a separate serverless function.
Three things you'll hit after go-live. First, Close fires the opportunity.updated webhook for every field change — a rep editing a note on a Won deal will re-fire your Won alert. Fix this in the code step by comparing status_label against previous_data.status_label in the webhook payload and only proceeding if they differ. Second, Close's webhook payload for value uses the raw currency value without the account's configured currency symbol — you need to hardcode the currency in your Intl.NumberFormat call or fetch it from the Close account settings endpoint. Third, if you add a second workflow later (say, a deal-at-risk alert), both workflows will share the same Close webhook subscription type and you may see duplicate processing. Keep related alerts in one workflow with internal branching rather than creating separate workflows per stage — it's easier to debug and costs fewer credits.
Ideas for what to build next
- →Route alerts to different channels by deal size — Add conditional logic in the Node.js step to send deals over $50,000 to #enterprise-deals and smaller deals to #sales-team. This takes about 10 minutes to add and eliminates the need to filter manually in Slack.
- →Add a daily pipeline digest — Create a second Pipedream workflow on a scheduled trigger that queries the Close API at 8am each morning and posts a summary of all open deals by stage to #sales-standup. This pairs well with the real-time alerts — real-time for wins, digest for pipeline review.
- →Post a Slack message reply when a deal closes and log it to a Google Sheet — Extend the Won stage alert to also append a row to a Google Sheet with deal name, value, owner, and close date. After 30 days you'll have a clean win log without any manual data entry from the sales team.
Related guides
How to Share Notion Meeting Notes to Slack with Pipedream
~15 min setup
How to Share Notion Meeting Notes to Slack with Power Automate
~15 min setup
How to Share Notion Meeting Notes to Slack with n8n
~20 min setup
How to Send Notion Meeting Notes to Slack with Zapier
~8 min setup
How to Share Notion Meeting Notes to Slack with Make
~12 min setup
How to Create Notion Tasks from Slack with Pipedream
~15 min setup