Intermediate~20 min setupCommunication & CRMVerified April 2026
Slack logo
Copper logo

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-time

Use case type

notification

Real-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.

/mo
505005K50K

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

Skip the setup

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.

Copper account with API access enabled and an API key generated under Settings > Integrations > API
Copper user account used for API calls must have the 'Lead' read permission scope
Slack app created at api.slack.com/apps with the chat:write OAuth scope and installed to your workspace
n8n instance running (self-hosted or n8n Cloud) with a publicly accessible URL so Copper can reach the webhook endpoint
Slack Member IDs for each sales rep who will receive lead alerts — find these in each rep's Slack profile under 'More > Copy member ID'

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Lead IDid
Lead Namename
Assignee IDassignee_id
Updated Atdate_modified
Rep Name
Slack Member ID
4 optional fields▸ show
Company Namecompany_name
Phone Numbersphone_numbers
Email Addressesemail
Lead Statusstatus

Step-by-Step Setup

1

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.

  1. 1Click '+ New Workflow' in the top-right corner of the n8n dashboard
  2. 2Click the large '+' button in the center of the blank canvas
  3. 3Type 'Webhook' in the search box and select the Webhook node
  4. 4Set 'HTTP Method' to POST in the node parameters panel
  5. 5Copy the webhook URL from the 'Webhook URL' field — you'll need it in Step 2
What you should see: You should see the Webhook node on the canvas with a URL displayed in the format https://[your-host]/webhook/[uuid]. The node shows 'Waiting for test event' when you click 'Test Step'.
Common mistake — n8n gives you two URLs: a test URL and a production URL. Use the test URL now during setup. Switch to the production URL before going live — Copper will not call the test URL once the workflow is active.
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.

  1. 1Log into Copper and click the gear icon in the bottom-left sidebar
  2. 2Select 'Integrations' then click 'API'
  3. 3Scroll to the 'Webhooks' section and click 'Add Webhook'
  4. 4Paste the n8n test webhook URL into 'Endpoint URL'
  5. 5Set Entity to 'Lead', check 'new' and 'update', then click Save
What you should see: The webhook appears in the list with a green status indicator. Copper sends a test ping to your endpoint — you'll see a request appear in n8n's Webhook node when you click 'Listen for Test Event'.
Common mistake — Copper sends update events for every field change on a lead, not just assignment. If you skip the assignee-change filter in n8n (Step 4), every edit to a lead — including notes and status — will fire a Slack message to the rep.
3

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.

  1. 1Click the Webhook node on the n8n canvas
  2. 2Click 'Listen for Test Event' in the node panel on the right
  3. 3Switch to Copper and create or reassign a lead
  4. 4Return to n8n and confirm the payload appears in the node output panel
What you should see: The Webhook node shows a green check and displays the Copper lead payload in the output panel, including fields like 'id', 'name', 'assignee_id', 'company_name', and 'updated_at'.
n8n
▶ Run once
executed
Slack
Copper
Copper
🔔 notification
received
4

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.

  1. 1Click the '+' connector from the Webhook node
  2. 2Search for 'IF' and select the IF node
  3. 3Set Value 1 to the expression {{ $json.assignee_id }}
  4. 4Set Condition to 'Is Not Empty'
  5. 5Connect the False output to a No Operation node
What you should see: The IF node shows two output branches labeled 'True' and 'False'. When you run a test with a lead that has an assignee, the data flows through the True branch.
Common mistake — Copper sends 'assignee_id' as null when a lead is unassigned. If you only check 'is not empty', unassignment events pass through as empty strings on some Copper plan tiers. Add a second condition: assignee_id is not equal to 0 to be safe.
Slack
SL
trigger
filter
Condition
matches criteria?
yes — passes through
no — skipped
Copper
CO
notified
5

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.

  1. 1Click '+' from the IF node's True output
  2. 2Search for 'HTTP Request' and select it
  3. 3Set Method to GET
  4. 4Set URL to https://api.copper.com/developer_api/v1/users/{{ $json.assignee_id }}
  5. 5Under Headers, add X-PW-AccessToken, X-PW-Application (developer_api), and X-PW-UserEmail
What you should see: The HTTP Request node returns a JSON object with the rep's 'id', 'name', and 'email' fields. You should see something like { "id": 482910, "name": "Jamie Torres", "email": "[email protected]" }.
Common mistake — Your Copper API key has a rate limit of 600 requests per minute. If you have a high volume of lead events back-to-back, this user lookup will count against that limit. Cache rep details in a static lookup table (Step 6) if you reassign more than 200 leads per hour.
6

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.

  1. 1Click '+' from the HTTP Request node output
  2. 2Search for 'Code' and select the Code node
  3. 3Set language to JavaScript
  4. 4Paste in the lookup map (see pro tip code below)
  5. 5Save the node and run a test to confirm the Slack ID resolves correctly
What you should see: The Code node outputs an object with slackUserId populated — for example { "slackUserId": "U04XYZABC", "repName": "Jamie Torres" }. If the email is not in the map, slackUserId should equal your fallback channel ID.
Common mistake — Slack Member IDs are workspace-specific. If your company has multiple Slack workspaces, a Member ID from one workspace will fail silently in another. Double-check you're copying IDs from the correct workspace.
Slack fields
text
user
channel
ts
thread_ts
available as variables:
1.props.text
1.props.user
1.props.channel
1.props.ts
1.props.thread_ts
7

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.

  1. 1Click '+' from the Slack ID lookup Code node
  2. 2Add another Code node
  3. 3Build the blocks array with a header block and a section block listing lead fields
  4. 4Include the Copper record deep link in a button block
  5. 5Output the final object with { channel, blocks } ready for the Slack node
What you should see: The Code node outputs a valid Slack payload object. When you paste the blocks value into Slack's Block Kit Builder at app.slack.com/block-kit-builder, the preview shows a formatted card with the lead's name, company, phone, and a 'View in Copper' button.
Common mistake — Slack Block Kit messages have a 3,000-character limit per block. Copper's custom field values can be long. Truncate any custom fields to 200 characters before inserting them into the blocks array or the Slack send will return a payload_too_large error.

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,
  }
}];
8

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.

  1. 1Click '+' from the message-builder Code node
  2. 2Search for 'Slack' and select the Slack node
  3. 3Set Resource to 'Message' and Operation to 'Send'
  4. 4Set Channel to {{ $json.channel }}
  5. 5Set Blocks to {{ JSON.stringify($json.blocks) }} and connect your Slack credentials
What you should see: After running a test, you should see the formatted Slack message appear as a DM to yourself (or the configured test channel) within 5-10 seconds of triggering a lead event in Copper.
Common mistake — The Slack node in n8n requires your Slack app to have the chat:write OAuth scope. If you created your Slack app with only incoming webhooks, the node will return a missing_scope error. You need a proper OAuth app — go to api.slack.com/apps to create one.
message template
🔔 New Record: {{text}} {{user}}
channel: {{channel}}
ts: {{ts}}
#sales
🔔 New Record: Jane Smith
Company: Acme Corp
9

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.

  1. 1Click the three-dot menu in the top-right of the canvas and select 'Settings'
  2. 2Enable 'Error Workflow' and point it to an existing error handler, or create a new one
  3. 3In the error workflow, add a Slack node that messages #n8n-alerts
  4. 4Include {{ $json.error.message }} and {{ $json.execution.id }} in the alert text
  5. 5Save both workflows
What you should see: When you manually break a node (e.g., enter a bad API key) and trigger a test, you should see a fallback Slack message appear in your admin channel within 30 seconds.
10

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.

  1. 1Click the Webhook node in n8n and copy the Production URL
  2. 2Go to Copper > Settings > Integrations > API > Webhooks
  3. 3Click Edit on your existing webhook and replace the URL with the production one
  4. 4Save in Copper
  5. 5Click the Activate toggle in n8n's top-right — it turns green when live
What you should see: The workflow shows 'Active' status in the n8n dashboard. Creating or reassigning a lead in Copper now fires a real Slack message to the assigned rep within 15 seconds.
Common mistake — If you deactivate the workflow in n8n for maintenance, Copper will still fire webhook events. Those events are lost — Copper does not queue or retry failed webhooks. Schedule maintenance during off-hours or temporarily pause lead creation in Copper.

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

VerdictWhy n8n for this workflow

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.

Cost

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.

Tradeoffs

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 SentAfter 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 DigestAdd 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 SourceExtend 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

Was this guide helpful?
Slack + Copper overviewn8n profile →