

How to Send Copper Deal Stage Alerts to Slack with n8n
Polls Copper every few minutes for deal stage changes and posts a formatted Slack message to your sales channel with the deal name, new stage, owner, and value.
Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.
Best for
Small to mid-size sales teams (5–30 reps) who want real-time Slack visibility into pipeline movement without building a custom integration.
Not ideal for
Teams needing true instant triggers — Copper has no native webhooks, so there will always be a polling delay of 1–5 minutes.
Sync type
scheduledUse case type
notificationReal-World Example
A 12-person SaaS sales team uses this to post to #pipeline-wins and #deals-at-risk in Slack whenever a Copper deal moves to 'Proposal Sent', 'Negotiation', or 'Won'. Before this workflow, reps relied on a weekly standup to surface stage changes — deals stalled in 'Proposal Sent' for days before anyone noticed. Now a Slack message fires within 3 minutes of the stage update, and the team lead can jump in to help or celebrate immediately.
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 n8n
Copy the pre-built n8n blueprint and paste it straight into n8n. 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 ID | id | |
| Deal Name | name | |
| Stage ID | stage_id | |
| Pipeline ID | pipeline_id | |
| Owner ID | assignee_id | |
| Updated At | date_modified | |
3 optional fields▸ show
| Deal Value | monetary_value |
| Close Date | close_date |
| Company Name | company_name |
Step-by-Step Setup
n8n Dashboard > Workflows > + New Workflow
Create a new n8n workflow
Log into your n8n instance (cloud at app.n8n.cloud or self-hosted). Click the '+ New Workflow' button in the top right of the Workflows dashboard. Give the workflow a name like 'Copper Deal Stage → Slack'. You'll land on a blank canvas with a default Start node — delete it, since you'll be using a Schedule trigger instead.
- 1Click '+ New Workflow' in the top right
- 2Type 'Copper Deal Stage → Slack' in the workflow name field at the top
- 3Click the existing Start node on the canvas and press Delete
Canvas > + > Schedule Trigger > Interval
Add a Schedule trigger
Click the '+' button on the canvas to open the node picker. Search for 'Schedule Trigger' and select it. Set the interval to every 3 minutes — this is the polling frequency that checks Copper for changes. Every 1 minute is available but will hit Copper's API rate limit faster; every 5 minutes is safer for teams with high deal volume.
- 1Click the '+' icon on the empty canvas
- 2Type 'Schedule' in the search box
- 3Select 'Schedule Trigger'
- 4Set 'Trigger Interval' to 'Minutes'
- 5Set the value to '3'
Copper Node > Credentials > Create New
Connect your Copper credentials
Click '+' after the Schedule Trigger to add a new node. Search for 'Copper' and select it. In the node configuration panel, click 'Credential for Copper API' and then 'Create New'. You'll need your Copper API key and the email address of your Copper account — both found in Copper under Settings > Integrations > API Keys. Paste them in and click Save.
- 1Click '+' after the Schedule Trigger node
- 2Search 'Copper' and select the Copper node
- 3Click 'Credential for Copper API' dropdown
- 4Click 'Create New Credential'
- 5Paste your Copper API Key and account email
- 6Click 'Save'
Copper Node > Resource > Opportunity > Get All > Filters
Configure Copper to list recently modified deals
In the Copper node, set Resource to 'Opportunity' and Operation to 'Get All'. Enable the 'Return All' toggle so you retrieve every result, not just the first page. Under Filters, add a filter for 'Updated At' greater than or equal to the current time minus 5 minutes — this scopes each poll to only recently changed deals. Without this filter, you'll return your entire pipeline on every execution.
- 1Set Resource to 'Opportunity'
- 2Set Operation to 'Get All'
- 3Toggle 'Return All' to ON
- 4Click 'Add Filter'
- 5Set filter field to 'Updated At'
- 6Set operator to 'is after'
- 7Set value to '={{ new Date(Date.now() - 5 * 60 * 1000).toISOString() }}'
Canvas > + > Filter > Conditions
Filter to deals where stage actually changed
Copper returns all recently updated deals regardless of what changed — a note, a contact link, or an activity update will all appear here. You need to filter down to only deals where the pipeline stage changed. Add an n8n 'Filter' node after the Copper node. The problem: Copper's API doesn't return a 'previous stage' field, so you'll use a static data store to compare. For now, filter to deals where 'stage_id' is not null as a baseline — you'll add deduplication in the next step.
- 1Click '+' after the Copper node
- 2Search 'Filter' and select the Filter node
- 3Click 'Add Condition'
- 4Set the left value to '={{ $json.stage_id }}'
- 5Set operator to 'is not empty'
Canvas > + > Remove Duplicates > Mode
Deduplicate with n8n's built-in deduplication node
Add a 'Remove Duplicates' node after the Filter node. Set the mode to 'Remove Items Seen in Previous Executions'. Set the deduplication key to a composite value: the deal ID combined with the stage ID, like '={{ $json.id }}-{{ $json.stage_id }}'. This ensures a notification only fires once per deal-per-stage combination. If a deal moves from Stage A → B → C, you'll get three separate notifications.
- 1Click '+' after the Filter node
- 2Search 'Remove Duplicates' and select it
- 3Set Mode to 'Remove Items Seen in Previous Executions'
- 4Set the Deduplication Key to '={{ $json.id }}-{{ $json.stage_id }}'
Canvas > + > HTTP Request
Look up pipeline stage name
Copper stores stage changes as a numeric stage_id, not a human-readable name. Add an HTTP Request node to fetch the pipeline stages from Copper's API. Call GET https://api.copper.com/developer_api/v1/pipelines and parse the response to find the stage name matching the deal's pipeline_id and stage_id. This lookup runs once per deal notification, so it adds one API call per triggered deal.
- 1Click '+' after the Remove Duplicates node
- 2Search 'HTTP Request' and select it
- 3Set Method to 'GET'
- 4Set URL to 'https://api.copper.com/developer_api/v1/pipelines'
- 5Under Headers, add 'X-PW-AccessToken' with your Copper API key
- 6Add header 'X-PW-UserEmail' with your Copper account email
- 7Add header 'Content-Type' as 'application/json'
Canvas > + > Code
Extract and format the stage name with a Code node
Add a Code node after the HTTP Request to resolve the stage name from the pipeline response and build the Slack message payload. This is where n8n's code node earns its keep — you can do the lookup and message formatting in one step. Paste the JavaScript from the Pro Tip section below. The output will be a single object with all the fields your Slack message needs.
- 1Click '+' after the HTTP Request node
- 2Search 'Code' and select it
- 3Set Language to 'JavaScript'
- 4Paste the code from the Pro Tip section into the code editor
- 5Click 'Test Step' to verify the stage name resolves correctly
Paste this into the Code node (Step 8) between the HTTP Request and Slack nodes. It resolves the stage name from the pipeline API response, formats the deal value, picks an emoji based on stage name, and builds the complete Slack message string — all in one pass.
JavaScript — Code Node// Inputs:▸ Show code
// Inputs: // - $input: the current deal item from Remove Duplicates // - $node['HTTP Request'].json: the pipeline data from Copper
... expand to see full code
// Inputs:
// - $input: the current deal item from Remove Duplicates
// - $node['HTTP Request'].json: the pipeline data from Copper
const deal = $input.first().json;
const pipelines = $node['HTTP Request'].json;
// Find the correct pipeline, then the correct stage within it
const pipeline = Array.isArray(pipelines)
? pipelines.find(p => p.id === deal.pipeline_id)
: null;
const stage = pipeline
? pipeline.stages.find(s => s.id === deal.stage_id)
: null;
const stageName = stage ? stage.name : 'Unknown Stage';
// Emoji map — add your own stage names here
const stageEmoji = {
'Won': '🏆',
'Lost': '❌',
'Negotiation': '🔴',
'Proposal Sent': '🔵',
'Qualified': '🟡',
'Discovery': '🟢',
};
const emoji = stageEmoji[stageName] || '📋';
// Format monetary value
const rawValue = deal.monetary_value;
const dealValue = rawValue != null
? `$${Number(rawValue).toLocaleString('en-US')}`
: 'No value set';
// Format close date (Copper returns Unix seconds)
const closeDate = deal.close_date
? new Date(deal.close_date * 1000).toLocaleDateString('en-US', {
month: 'short', day: 'numeric', year: 'numeric'
})
: 'No close date';
// Build deep link back to Copper deal
const dealUrl = `https://app.copper.com/companies/app#/deals/${deal.id}`;
// Owner name: Copper returns assignee_id (numeric).
// For a real name, you'd need a second API call to /people/{id}.
// As a placeholder, we flag it clearly:
const ownerLabel = deal.assignee ? deal.assignee.name : `Rep ID ${deal.assignee_id}`;
// Build Slack message
const slackMessage = [
`${emoji} *${deal.name}* moved to *${stageName}*`,
`💰 ${dealValue} · 👤 ${ownerLabel} · 📅 Close: ${closeDate}`,
`🔗 View in Copper: ${dealUrl}`
].join('\n');
return [{
json: {
dealId: deal.id,
dealName: deal.name,
stageName,
dealValue,
ownerLabel,
closeDate,
slackMessage,
dealUrl
}
}];Canvas > + > Slack > Message > Send
Connect your Slack credentials and configure the message
Add a Slack node after the Code node. Set Resource to 'Message' and Operation to 'Send'. Click 'Credential for Slack API' and create a new credential using your Slack Bot Token (starts with xoxb-). Set the Channel field to your target channel name, like #pipeline-updates. Set the Message Text field to '={{ $json.slackMessage }}' to pull in the formatted message from the Code node.
- 1Click '+' after the Code node
- 2Search 'Slack' and select the Slack node
- 3Set Resource to 'Message'
- 4Set Operation to 'Send'
- 5Click 'Credential for Slack API' and select or create your bot credential
- 6Set Channel to '#pipeline-updates' (or your channel name)
- 7Set Text to '={{ $json.slackMessage }}'
Workflow Settings > Error Workflow
Add error handling with an Error Trigger
Add a separate Error Trigger workflow to catch failures. In your main workflow, click the three-dot menu in the top right and select 'Settings'. Enable 'Error Workflow' and point it to a separate n8n workflow that sends a Slack DM to the workflow owner if something breaks. Without this, polling failures are silent — you won't know notifications stopped until someone asks why Slack went quiet.
- 1Click the three-dot menu (⋮) in the top right of the workflow editor
- 2Click 'Settings'
- 3Toggle 'Error Workflow' to ON
- 4Select an existing error-handler workflow or click 'Create New'
- 5In the error workflow, add a Slack node that DMs the admin with '={{ $json.execution.error.message }}'
Workflow Editor > Active Toggle > Executions Tab
Activate and verify the workflow
Click the 'Inactive' toggle in the top right of the workflow editor to switch it to 'Active'. n8n will now run the Schedule Trigger every 3 minutes. To verify it's working, manually move a deal to a new stage in Copper, then wait up to 5 minutes and check your Slack channel. The notification should appear with the deal name, new stage, and owner. Check the Executions tab to see each run's output and confirm no errors.
- 1Click the 'Inactive' toggle in the top right — it should turn green and read 'Active'
- 2Open Copper and manually move a test deal to a different pipeline stage
- 3Wait 3–5 minutes
- 4Check your Slack channel for the notification
- 5Click 'Executions' in the left sidebar to review run logs
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 n8n for this if you self-host your tools, want zero per-task pricing, or need the Code node to do non-trivial data shaping — like resolving Copper's numeric stage IDs to human-readable names in the same step that builds the Slack message. n8n also lets you version-control workflows as JSON, which matters if you're managing this alongside engineering infrastructure. The one case where you'd pick something else: if your team has zero technical capacity and you need this running in 20 minutes — Zapier's Copper integration has a pre-built 'Updated Deal' trigger that requires no code at all.
The cost math is straightforward. This workflow runs every 3 minutes — that's 480 executions per day, 14,400 per month. On n8n Cloud's Starter plan ($20/month), you get 2,500 executions. You'll need the Pro plan ($50/month) to cover 14,400 executions. Self-hosted n8n is free with no execution limits — the only cost is your server ($5–$10/month on a small VPS). Zapier at this volume would cost $49/month on the Professional plan. Make would handle this workflow in a Free tier scenario with careful scoping, but Make's Copper module has fewer filtering options, so you may burn more operations per run.
Zapier has one genuine advantage here: its Copper 'Updated Opportunity' trigger is event-driven within Zapier's poller — the UI is simpler and the setup takes half the time. Make handles conditional branching better visually when you want to route Won vs. Stalled deals to different channels — the Router module is cleaner than n8n's IF node for multi-path logic. Power Automate has no official Copper connector, so you'd be doing custom HTTP calls for every step — skip it. Pipedream's Copper polling source is well-maintained and supports filtering by stage change with less code. But n8n wins here for teams that want self-hosted control, need the Code node for stage name resolution, or are already running n8n for other workflows.
Three things you'll hit after going live. First, Copper's 'date_modified' field updates on any change — a note added by a rep, a tag change, or a linked email all trigger the poll filter. Your deduplication node handles this, but you'll see executions where items get filtered at the Remove Duplicates step more often than you expect. Second, the Copper /pipelines endpoint occasionally returns an empty stages array for newly created pipelines that have no deals yet — your Code node will output 'Unknown Stage' and the Slack message will look broken. Add a guard clause. Third, Slack's API throttles bots that post more than 1 message per second to the same channel. If a large batch of deals change stage simultaneously (end of quarter pipeline cleanup, for example), add a Wait node set to 1 second between Slack posts to avoid 429 rate limit errors.
Ideas for what to build next
- →Route 'Won' deals to a dedicated #wins channel — Add an IF node after the Code node that checks if stageName equals 'Won' and routes those to #sales-wins while all other stages go to #pipeline-updates. Takes about 10 minutes to add.
- →Fetch the owner's real name via Copper People API — The current setup uses the assignee_id as a fallback label. Add a second HTTP Request node calling GET /v1/people/{assignee_id} to resolve it to a real name and display it in the Slack message.
- →Send a daily digest instead of per-deal messages — Change the Schedule Trigger to run once per day at 9am, collect all stage changes in the past 24 hours, and post a single formatted summary to Slack using an n8n Aggregate node before the Code node.
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