

How to Send Copper Lead Alerts to Slack with n8n
Fires a Slack DM or channel message to the assigned sales rep whenever a lead is created or reassigned in Copper, using a Copper webhook caught by n8n.
Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.
Best for
Sales teams on Google Workspace who live in Slack and need instant notification when a Copper lead lands in their queue
Not ideal for
Teams that reassign leads in bulk — each reassignment fires a separate webhook event and you'll hit Slack rate limits above ~50 reassignments per minute
Sync type
real-timeUse case type
notificationReal-World Example
A 12-person SaaS sales team routes inbound leads from a web form into Copper, where a round-robin assignment rule distributes them to reps. Before this workflow, reps refreshed Copper manually and often took 20-40 minutes to pick up a new lead. After setup, each rep gets a Slack DM within 15 seconds of assignment, including the lead name, company, phone, and a direct link to the Copper record.
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.
Field Mapping
Map these fields between your apps.
| Field | API Name | |
|---|---|---|
| Required | ||
| Lead ID | id | |
| Lead Name | name | |
| Assignee ID | assignee_id | |
| Updated At | date_modified | |
| Rep Name | ||
| Slack Member ID | ||
4 optional fields▸ show
| Company Name | company_name |
| Phone Numbers | phone_numbers |
| Email Addresses | email |
| Lead Status | status |
Step-by-Step Setup
n8n Canvas > + Node > Webhook
Create the n8n workflow and add a Webhook node
Open n8n and click the orange '+ New Workflow' button in the top right. On the blank canvas, click the '+' node button and search for 'Webhook' in the node panel. Select the Webhook node — this is what Copper will POST to when a lead event fires. Set the HTTP Method to POST and note the generated webhook URL (it looks like https://your-n8n-host/webhook/xxxxxxxx). You'll paste this into Copper in the next step.
- 1Click '+ New Workflow' in the top-right corner of the n8n dashboard
- 2Click the large '+' button in the center of the blank canvas
- 3Type 'Webhook' in the search box and select the Webhook node
- 4Set 'HTTP Method' to POST in the node parameters panel
- 5Copy the webhook URL from the 'Webhook URL' field — you'll need it in Step 2
Copper > Settings > Integrations > API > Webhooks > Add Webhook
Register the webhook in Copper
In Copper, go to Settings > Integrations > API and scroll to the Webhooks section. Click 'Add Webhook'. Paste your n8n webhook URL into the Endpoint URL field. Under 'Entity', select 'Lead'. Under 'Event', check both 'new' (lead created) and 'update' (lead updated — covers reassignment). Save the webhook. Copper does not natively filter update events by field, so n8n will handle the logic to check if the assignee changed.
- 1Log into Copper and click the gear icon in the bottom-left sidebar
- 2Select 'Integrations' then click 'API'
- 3Scroll to the 'Webhooks' section and click 'Add Webhook'
- 4Paste the n8n test webhook URL into 'Endpoint URL'
- 5Set Entity to 'Lead', check 'new' and 'update', then click Save
n8n Canvas > Webhook Node > Listen for Test Event
Trigger a test lead event from Copper
Back in n8n, click the Webhook node and hit 'Listen for Test Event'. Then in Copper, open any existing lead and either create a new one or change its assignee. Copper will POST the lead payload to your webhook URL immediately. In n8n, the Webhook node will display the raw JSON it received. Look for the 'assignee_id' field — this is a numeric ID, not the rep's name. You'll resolve it to a readable name in a later step.
- 1Click the Webhook node on the n8n canvas
- 2Click 'Listen for Test Event' in the node panel on the right
- 3Switch to Copper and create or reassign a lead
- 4Return to n8n and confirm the payload appears in the node output panel
n8n Canvas > + Node > IF
Add an IF node to filter for assignee changes
Connect an IF node after the Webhook node. You need to confirm the lead was actually assigned or reassigned before sending any Slack message. Copper's update payload includes 'assignee_id' when the field was part of the change. Set the IF condition to check that the expression {{ $json.assignee_id }} is not empty. Leads with no assignee or events unrelated to assignment will exit the False branch and stop there — connect the False branch to a No Operation node to terminate cleanly.
- 1Click the '+' connector from the Webhook node
- 2Search for 'IF' and select the IF node
- 3Set Value 1 to the expression {{ $json.assignee_id }}
- 4Set Condition to 'Is Not Empty'
- 5Connect the False output to a No Operation node
n8n Canvas > + Node > HTTP Request
Fetch the assigned rep's details from Copper API
The Copper webhook gives you assignee_id as a number (e.g., 482910), not a name or email. You need to resolve this to a real name before messaging anyone in Slack. Add an HTTP Request node after the IF True branch. Set the method to GET and the URL to https://api.copper.com/developer_api/v1/users/{{ $json.assignee_id }}. Add three headers: X-PW-AccessToken (your Copper API key), X-PW-Application set to developer_api, and X-PW-UserEmail set to the email of the Copper account the API key belongs to. This returns the rep's name and email.
- 1Click '+' from the IF node's True output
- 2Search for 'HTTP Request' and select it
- 3Set Method to GET
- 4Set URL to https://api.copper.com/developer_api/v1/users/{{ $json.assignee_id }}
- 5Under Headers, add X-PW-AccessToken, X-PW-Application (developer_api), and X-PW-UserEmail
n8n Canvas > + Node > Code
Map the rep's email to their Slack member ID
Slack messages sent to a user must use their Slack Member ID (e.g., U04XYZABC), not their email address. Add a Code node after the HTTP Request node. Write a small lookup object that maps each rep's Copper email to their Slack Member ID — you can find Slack Member IDs by clicking a user's profile in Slack, then 'More' and 'Copy member ID'. This avoids a Slack API call on every run and keeps latency low. If the email isn't in your map, the code outputs a fallback channel like #sales-leads.
- 1Click '+' from the HTTP Request node output
- 2Search for 'Code' and select the Code node
- 3Set language to JavaScript
- 4Paste in the lookup map (see pro tip code below)
- 5Save the node and run a test to confirm the Slack ID resolves correctly
n8n Canvas > + Node > Code
Build the Slack message with a Code node
Add another Code node to assemble the Slack Block Kit message. Block Kit lets you format the alert with bold fields, a direct link to the Copper record, and a clear call to action. Pull the lead name, company, phone, and the rep name from earlier nodes using n8n's $node syntax. The Copper record URL follows the pattern https://app.copper.com/companies/[your-workspace-id]/app#/leads/[lead-id] — find your workspace ID in Copper's URL when you're logged in. Output the full Slack blocks payload as a JSON object.
- 1Click '+' from the Slack ID lookup Code node
- 2Add another Code node
- 3Build the blocks array with a header block and a section block listing lead fields
- 4Include the Copper record deep link in a button block
- 5Output the final object with { channel, blocks } ready for the Slack node
Paste this into two Code nodes: the first (Steps 6-7) resolves the Copper assignee_id to a Slack Member ID using a static lookup map and falls back to a #sales-leads channel if the rep isn't found; the second builds the full Slack Block Kit payload with the lead details and a direct Copper link. Place the first Code node immediately after the HTTP Request node, and the second Code node after the first.
JavaScript — Code Node// CODE NODE 1: Resolve Copper rep email to Slack Member ID▸ Show code
// CODE NODE 1: Resolve Copper rep email to Slack Member ID // Paste after the HTTP Request node (Step 6) const repEmail = $node['HTTP Request'].json.email?.toLowerCase();
... expand to see full code
// CODE NODE 1: Resolve Copper rep email to Slack Member ID
// Paste after the HTTP Request node (Step 6)
const repEmail = $node['HTTP Request'].json.email?.toLowerCase();
const repName = $node['HTTP Request'].json.name || 'Unknown Rep';
// Maintain this map with your team's actual emails and Slack Member IDs
// Find Slack Member IDs: click profile > More > Copy member ID
const emailToSlackId = {
'[email protected]': 'U04XYZABC',
'[email protected]': 'U05DEFGHI',
'[email protected]': 'U06JKLMNO',
'[email protected]': 'U07PQRSTU',
};
const FALLBACK_CHANNEL = 'C08SALESLEADS'; // #sales-leads channel ID
const slackUserId = emailToSlackId[repEmail] || FALLBACK_CHANNEL;
const isFallback = slackUserId === FALLBACK_CHANNEL;
return [{
json: {
slackUserId,
repName,
repEmail,
isFallback,
}
}];
// -----------------------------------------------
// CODE NODE 2: Build Slack Block Kit message payload
// Paste after Code Node 1 (Step 7)
// -----------------------------------------------
const lead = $node['Webhook'].json;
const { slackUserId, repName, isFallback } = $node['Code'].json;
// Replace 12345 with your actual Copper workspace ID (found in the Copper URL)
const COPPER_WORKSPACE_ID = '12345';
const copperUrl = `https://app.copper.com/companies/${COPPER_WORKSPACE_ID}/app#/leads/${lead.id}`;
const phone = lead.phone_numbers?.[0]?.number || 'No phone on file';
const email = lead.email?.[0]?.email || 'No email on file';
const company = lead.company_name || 'No company listed';
const status = lead.status || 'Unknown';
// Truncate long values to avoid Slack's 3000-char block limit
const truncate = (str, max = 200) => str.length > max ? str.slice(0, max) + '…' : str;
const headerText = isFallback
? `⚠️ Unrouted Lead — no Slack ID found for ${repName}`
: `🎯 New Lead Assigned to You, ${repName}`;
const blocks = [
{
type: 'header',
text: { type: 'plain_text', text: headerText, emoji: true }
},
{
type: 'section',
fields: [
{ type: 'mrkdwn', text: `*Lead:*\n${truncate(lead.name)}` },
{ type: 'mrkdwn', text: `*Company:*\n${truncate(company)}` },
{ type: 'mrkdwn', text: `*Phone:*\n${phone}` },
{ type: 'mrkdwn', text: `*Email:*\n${email}` },
{ type: 'mrkdwn', text: `*Status:*\n${status}` },
]
},
{
type: 'actions',
elements: [
{
type: 'button',
text: { type: 'plain_text', text: 'View in Copper', emoji: true },
url: copperUrl,
action_id: 'view_copper_lead'
}
]
},
{
type: 'context',
elements: [
{ type: 'mrkdwn', text: `Lead ID: ${lead.id} • Assigned at <!date^${Math.floor(Date.now()/1000)}^{time} on {date_short}|just now>` }
]
}
];
return [{
json: {
channel: slackUserId,
blocks,
}
}];n8n Canvas > + Node > Slack > Message > Send
Add the Slack node and send the message
Add a Slack node after the message-builder Code node. Set the Resource to 'Message' and Operation to 'Send'. In the Channel field, use the expression {{ $json.channel }} to pull from your Code node output — this will be the rep's Slack Member ID for a DM, or a channel ID for a group alert. In the Message field, set it to use Block Kit by selecting 'Blocks' and using the expression {{ JSON.stringify($json.blocks) }}. Authenticate by connecting your Slack OAuth app credentials.
- 1Click '+' from the message-builder Code node
- 2Search for 'Slack' and select the Slack node
- 3Set Resource to 'Message' and Operation to 'Send'
- 4Set Channel to {{ $json.channel }}
- 5Set Blocks to {{ JSON.stringify($json.blocks) }} and connect your Slack credentials
channel: {{channel}}
ts: {{ts}}
n8n Canvas > Workflow Settings > Error Workflow
Add error handling with a Slack fallback alert
Connect an Error Trigger node that sends a Slack message to an admin channel (like #n8n-alerts) if any step fails — particularly the Copper API lookup or the Slack send itself. In n8n, click the three-dot menu on the workflow canvas and toggle on 'Error Workflow', then point it to a separate error-handling workflow or add error outputs directly. At minimum, log the lead ID and the error message so you can replay the event manually if a rep misses a hot lead because of an API failure.
- 1Click the three-dot menu in the top-right of the canvas and select 'Settings'
- 2Enable 'Error Workflow' and point it to an existing error handler, or create a new one
- 3In the error workflow, add a Slack node that messages #n8n-alerts
- 4Include {{ $json.error.message }} and {{ $json.execution.id }} in the alert text
- 5Save both workflows
Copper > Settings > Integrations > API > Webhooks | n8n Canvas > Activate Toggle
Switch the webhook to the production URL and activate
Go back to the Webhook node in n8n and copy the Production webhook URL (it's listed just below the test URL in the node panel). Return to Copper Settings > Integrations > API > Webhooks and edit the webhook you created in Step 2. Replace the test URL with the production URL. Then in n8n, click the toggle in the top-right corner of the canvas to activate the workflow. The toggle turns green when active.
- 1Click the Webhook node in n8n and copy the Production URL
- 2Go to Copper > Settings > Integrations > API > Webhooks
- 3Click Edit on your existing webhook and replace the URL with the production one
- 4Save in Copper
- 5Click the Activate toggle in n8n's top-right — it turns green when live
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 infrastructure, want zero per-task cost at any volume, and are comfortable writing 20-30 lines of JavaScript. The email-to-Slack-ID lookup and Block Kit message construction are both code steps — n8n's Code node handles them in one place, cleanly. The one case where you'd skip n8n: if your team has no one who can read JavaScript and debug a failed HTTP request header. In that scenario, Zapier's Copper integration requires zero code and takes 12 minutes to set up.
n8n Cloud costs $20/month flat for up to 10,000 executions. This workflow uses 1 execution per lead event. A sales team processing 300 lead assignments per month spends effectively $0.07 per alert — you won't get close to the execution limit unless you're a high-volume SDR team doing 500+ daily lead touches. Self-hosted n8n is free indefinitely: your only cost is the server ($5-10/month on a small VPS). Zapier would run you $49/month minimum (Professional plan) for Copper access, since Copper is a premium app on Zapier. Make costs $9/month at 10,000 operations — cheaper than Zapier but more expensive than self-hosted n8n.
Zapier's Copper trigger is faster to configure — their 'New Lead' and 'Updated Lead' triggers are pre-built and don't require manual webhook registration. Make's scenario builder lets you visually branch on 'assignee changed' with a filter module in under a minute, no code. Power Automate has a Copper connector in preview but it's unreliable — the trigger polling interval is 15 minutes minimum, which kills the 'instant alert' value entirely. Pipedream's HTTP source handles the Copper webhook well and their Slack step is one of the cleanest in the ecosystem, but you're writing async/await code for the user lookup anyway — at that point, n8n's Code node is equivalent. n8n wins here because it combines webhook handling, HTTP requests, code, and Slack messaging in a single self-hosted tool with no per-task fees.
Three things you'll hit after setup. First: Copper's update webhook fires on every field change, not just assignee changes — day one you'll flood your reps with Slack messages every time someone adds a note. The IF node filter in Step 4 is not optional. Second: Copper's API rate limit is 600 requests per minute, but their webhook delivery can spike during business hours when many leads are touched simultaneously. If you have 5 reps and a busy morning, you might fire 30-40 user-lookup API calls in 60 seconds — that's fine, but at 200 concurrent leads it becomes a problem. Third: Slack deactivated accounts fail silently on the n8n Slack node unless you have the Error Workflow set up. A rep who quit last month still appears in your lookup map, and every lead assigned to them just disappears into the void. Audit the lookup map whenever someone leaves the sales team.
Ideas for what to build next
- →Add a Copper Activity Log Entry on Message Sent — After the Slack message fires, write back to Copper's Activity API to log that the rep was notified — this creates an audit trail inside the CRM and helps managers confirm alerts are firing without checking Slack history.
- →Build a Daily Unresponded Leads Digest — Add a second scheduled workflow that queries Copper each morning for leads assigned more than 4 hours ago with no activity logged, and sends a digest to the rep and their manager in Slack — catches cases where the real-time alert was missed.
- →Route Alerts by Lead Score or Source — Extend the IF node logic to branch on lead source (e.g., demo request vs. cold inbound) and send high-priority leads to a dedicated #hot-leads Slack channel in addition to the rep DM, giving the whole team visibility on top prospects.
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