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

How to Send Copper Deal Stage Alerts to Slack with Pipedream

Fires a Slack message to a sales channel every time a Copper deal moves to a new pipeline stage.

Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.

Best for

Sales teams of 5-30 reps who live in Slack and want instant visibility into deal movement without logging into Copper.

Not ideal for

Teams who need two-way sync or want to update Copper stage from Slack — use a different workflow for that.

Sync type

real-time

Use case type

notification

Real-World Example

💡

A 12-person B2B SaaS sales team posts every deal stage change to #deals-live in Slack. Before this automation, reps manually refreshed Copper 4-5 times a day and still missed when deals moved to Proposal or Closed Won for hours. Now the message fires within 10 seconds of the stage change, includes the deal value and owner name, and the team uses it to jump on stalled deals before they go cold.

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

Copper account with API access enabled and at least one active pipeline with multiple stages configured
Copper user credentials with permission to create webhooks (Settings > Integrations access required)
Slack workspace where you have permission to install apps and post to the target channel
Pipedream account — free tier works for up to 10,000 events/month; paid plans start at $19/month
Copper API key for the enrichment lookup step (found in Copper > Settings > Integrations > API Keys)

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Deal Namename
Pipeline Stage IDpipeline_stage_id
Assignee IDassignee_id
Deal IDid
4 optional fields▸ show
Deal Valuemonetary_value
Close Dateclose_date
Pipeline IDpipeline_id
Tagstags

Step-by-Step Setup

1

pipedream.com > Workflows > New Workflow

Create a new Pipedream workflow

Go to pipedream.com and sign in. Click 'Workflows' in the left sidebar, then click the blue 'New Workflow' button in the top right. You'll land on an empty canvas with a trigger slot at the top and a prompt to add your first step. Give the workflow a name like 'Copper Deal Stage → Slack' so you can find it later.

  1. 1Click 'Workflows' in the left navigation
  2. 2Click 'New Workflow' in the top right
  3. 3Click the workflow title at the top and rename it to 'Copper Deal Stage → Slack'
What you should see: You see a blank workflow canvas with a gray 'Add Trigger' block at the top and a '+ Add a step' button below it.
2

Workflow Canvas > Add Trigger > Copper > Opportunity Stage Changed

Add the Copper webhook trigger

Click the 'Add Trigger' block. Search for 'Copper' in the app search bar. Select the Copper app, then choose the 'Opportunity Stage Changed' event source — this is the specific trigger that fires only on stage transitions, not on every deal update. Pipedream will generate a unique webhook URL for Copper to call.

  1. 1Click the gray 'Add Trigger' block
  2. 2Type 'Copper' in the search box
  3. 3Select 'Copper' from the app results
  4. 4Choose 'Opportunity Stage Changed' from the trigger list
  5. 5Click 'Connect Copper Account' and complete OAuth
What you should see: You see a green 'Connected' badge next to your Copper account name, and Pipedream displays a unique webhook endpoint URL ending in .m.pipedream.net.
Common mistake — Copper's native webhook system requires you to register the Pipedream URL inside Copper manually — Pipedream cannot do this automatically. Copy the webhook URL now before leaving this screen.
Pipedream
+
click +
search apps
Slack
SL
Slack
Add the Copper webhook trigger
Slack
SL
module added
3

Copper > Settings > Integrations > Webhooks > Add Webhook

Register the webhook URL in Copper

Open Copper in a new tab. Go to Settings > Integrations > Webhooks. Click 'Add Webhook'. Paste the Pipedream webhook URL into the Endpoint field. Under 'Events', check 'Opportunity Updated' — this is the event Copper fires when a stage changes. Save the webhook. Copper will send a verification ping to confirm the endpoint is live.

  1. 1Navigate to Settings in the Copper left sidebar
  2. 2Click 'Integrations', then 'Webhooks'
  3. 3Click 'Add Webhook'
  4. 4Paste your Pipedream webhook URL into the Endpoint URL field
  5. 5Check the 'Opportunity Updated' event box and click Save
What you should see: Copper shows the webhook with a green status indicator. Back in Pipedream, if you click 'Listen for events', you'll see a test payload arrive within 30 seconds of triggering a stage change in Copper.
Common mistake — Copper sends 'Opportunity Updated' for ALL field changes, not just stage changes. You must filter for stage changes in a later step — otherwise your Slack channel gets spammed on every deal edit.
4

Copper > Any Deal > Stage Dropdown | Pipedream > Workflow > Trigger > Event Inspector

Send a test event from Copper

In Copper, open any deal and move it to a different pipeline stage manually. This sends a real webhook payload to Pipedream. Switch back to Pipedream and click 'Refresh' next to the trigger step — you should see the raw payload in the event inspector on the right side of the screen. Expand the payload to confirm it includes 'pipeline_stage_id' and 'name' fields.

  1. 1In Copper, open a test deal
  2. 2Click the pipeline stage dropdown and select a different stage
  3. 3Switch to Pipedream and click 'Refresh' on the trigger step
  4. 4Click the incoming event to expand the payload
What you should see: You see a JSON payload in Pipedream's event inspector that includes fields like 'id', 'name', 'assignee_id', 'monetary_value', and 'pipeline_stage_id'.
Common mistake — Copper's webhook payload does NOT include the stage name — only the stage ID as an integer. You'll need a lookup step to convert it to a readable name like 'Proposal Sent' before sending to Slack.
Pipedream
▶ Deploy & test
executed
Slack
Copper
Copper
🔔 notification
received
5

Workflow Canvas > + Add a step > Run Node.js code

Add a Node.js code step to filter and enrich the event

Click '+ Add a step' below the trigger, then select 'Run Node.js code'. This step does two things: it filters out non-stage-change events (since Copper fires on any deal update), and it resolves the stage ID to a human-readable name by calling the Copper API. You'll use the async/await pattern with Pipedream's built-in axios helper. Paste the code from the Pro Tip section into the code editor.

  1. 1Click '+ Add a step'
  2. 2Click 'Run Node.js code'
  3. 3Clear the default code in the editor
  4. 4Paste the enrichment and filter code into the editor
  5. 5Click 'Test' to run it against your test event
What you should see: The step output panel shows a JSON object with 'stageName' as a readable string like 'Proposal Sent', 'dealName', 'dealValue', and 'ownerName'. If the event is not a stage change, the step outputs 'skip: true'.
Common mistake — Copper's API rate limit is 600 requests per minute per account. If your team makes bulk stage changes (e.g. during a pipeline cleanup), this lookup call fires once per deal — at 600 deals you'd hit the cap. For teams moving more than 200 deals at once, add a 100ms delay between lookups.

Paste this into the Node.js code step (Step 5). It filters non-stage-change events, resolves the stage ID and assignee ID to human-readable names via the Copper REST API, and returns a clean object ready for the Slack step to consume directly.

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.body;

    // Copper fires on all opportunity updates — skip if stage didn't change
    const updatedFields = event.updated_fields || [];
    if (!updatedFields.includes('pipeline_stage_id')) {
      return { skip: true };
    }

    const COPPER_API_KEY = process.env.COPPER_API_KEY;
    const COPPER_EMAIL = process.env.COPPER_USER_EMAIL;

    const headers = {
      'X-PW-AccessToken': COPPER_API_KEY,
      'X-PW-Application': 'developer_api',
      'X-PW-UserEmail': COPPER_EMAIL,
      'Content-Type': 'application/json',
    };

    // Fetch pipeline stages to resolve stage ID to name
    const pipelinesRes = await axios.get(
      'https://api.copper.com/developer_api/v1/pipelines',
      { headers }
    );

    let stageName = `Stage ID ${event.pipeline_stage_id}`;
    for (const pipeline of pipelinesRes.data) {
      const match = pipeline.stages.find(s => s.id === event.pipeline_stage_id);
      if (match) {
        stageName = match.name;
        break;
      }
    }

    // Resolve assignee ID to a full name
    let ownerName = 'Unassigned';
    if (event.assignee_id) {
      try {
        const userRes = await axios.get(
          `https://api.copper.com/developer_api/v1/users/${event.assignee_id}`,
          { headers }
        );
        ownerName = userRes.data.name || 'Unassigned';
      } catch (err) {
        console.log('Could not resolve owner:', err.message);
      }
    }

    // Format deal value
    const dealValue = event.monetary_value
      ? `$${Number(event.monetary_value).toLocaleString('en-US')}`
      : 'No value set';

    // Build Copper deep link
    const copperLink = `https://app.copper.com/companies/deals/${event.id}`;

    return {
      skip: false,
      dealName: event.name,
      stageName,
      ownerName,
      dealValue,
      copperLink,
      dealId: event.id,
    };
  },
});
Slack
SL
trigger
filter
Condition
matches criteria?
yes — passes through
no — skipped
Copper
CO
notified
6

Workflow Canvas > + Add a step > Filter

Add a filter step to skip non-stage changes

Click '+ Add a step' and select 'Filter' from Pipedream's built-in helpers. Configure it to continue only when the previous code step returns 'skip: false'. This prevents the workflow from posting to Slack when Copper fires on address updates, note additions, or any other field change that isn't a stage transition. Set the condition to: steps.code.$return_value.skip === false.

  1. 1Click '+ Add a step'
  2. 2Click 'Filter' from the helper options
  3. 3Set the condition field to steps.code.$return_value.skip
  4. 4Set the operator to 'equals'
  5. 5Set the value to false
What you should see: When you test with a stage-change event, the filter shows 'Condition passed — continuing'. When you test with a non-stage-change event, it shows 'Condition not met — workflow stopped here'.
Common mistake — Filters are the most common place setups break. Double-check the field name and value exactly match what your app sends — a single capital letter difference will block everything.
7

Workflow Canvas > + Add a step > Slack > Send Message to Channel

Add the Slack step and connect your account

Click '+ Add a step' and search for 'Slack'. Select the Slack app, then choose 'Send Message to Channel' as the action. Click 'Connect Slack Account' — this opens an OAuth popup. Authorize Pipedream to post to your workspace. You'll need to be a Slack admin or have permission to authorize apps for your workspace.

  1. 1Click '+ Add a step'
  2. 2Search for 'Slack' and select it
  3. 3Choose 'Send Message to Channel'
  4. 4Click 'Connect Slack Account'
  5. 5Complete the Slack OAuth flow and select your workspace
What you should see: The Slack step shows a green 'Connected' badge with your workspace name. The Channel and Text fields appear below ready for configuration.
Common mistake — If your Slack workspace uses Enterprise Grid, individual workspace OAuth may fail. You'll need a Slack admin to approve the Pipedream app installation at the org level first.
Pipedream settings
Connection
Choose a connection…Add
click Add
Slack
Log in to authorize
Authorize Pipedream
popup window
Connected
green checkmark
8

Workflow Canvas > Slack Step > Channel + Text Fields

Configure the Slack message content

In the Channel field, type the exact channel name including the # symbol — e.g. #deals-live. In the Text field, build the message using dynamic values from the code step. Reference fields like {{steps.code.$return_value.dealName}}, {{steps.code.$return_value.stageName}}, and {{steps.code.$return_value.dealValue}}. Use Slack's mrkdwn format for bold text — wrap field names in asterisks.

  1. 1Click the Channel field and type your target channel name (e.g. #deals-live)
  2. 2Click the Text field
  3. 3Type your message template using double-curly references to code step outputs
  4. 4Toggle 'mrkdwn' to ON so Slack renders bold and emoji formatting
  5. 5Click 'Test' to send a real message to the channel
What you should see: A message appears in your Slack channel that reads something like: '📈 *Acme Corp* moved to *Proposal Sent* — $24,000 | Owner: Sarah Kim'.
Common mistake — Map fields using the variable picker — don't type field names manually. Hand-typed variable names often have invisible spacing errors that produce blank output.
9

Copper > Deal > Stage Dropdown | Pipedream > Workflow > Event History

Test the full workflow end to end

Go back to Copper and move a deal to a different stage. Watch Pipedream's workflow event log — you should see all steps turn green within 10 seconds. Check the Slack channel to confirm the message arrived with correct deal name, stage name, value, and owner. If the filter step stops the workflow, confirm the test event is actually a stage change and not a different field update.

  1. 1Open a real deal in Copper
  2. 2Change its pipeline stage
  3. 3Switch to Pipedream and click 'Event History' in the left panel
  4. 4Click the new event to see each step's result
  5. 5Check your Slack channel for the message
What you should see: All steps show green checkmarks. The Slack message appears in the channel within 10 seconds with accurate deal data. The Pipedream event log shows the workflow completed in under 2 seconds.
10

Workflow Canvas > Deploy Button (top right)

Deploy the workflow to production

Click the gray 'Deploy' button in the top right of the Pipedream canvas. The button turns blue and the workflow status changes from 'Development' to 'Active'. From this point, every Copper stage change fires the workflow automatically with no manual intervention. Monitor the first 10-15 real events in the Event History tab to confirm no unexpected errors surface from real production data.

  1. 1Click the 'Deploy' button in the top right
  2. 2Confirm the status badge changes to 'Active'
  3. 3Open Event History in the left sidebar
  4. 4Watch the first real events arrive and confirm all steps pass
What you should see: The workflow status shows 'Active' in green. Real deal stage changes from your team generate Slack messages within 10 seconds. Event History shows completed runs with no errors.
Common mistake — Do not leave the workflow in 'Development' mode thinking it will still run — workflows in Development mode only fire when you manually trigger them from the canvas. You must click Deploy.

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 Pipedream for this if your team has someone comfortable reading and writing JavaScript. The real advantage here is the enrichment step — Copper's webhook only sends stage IDs, not stage names, and resolving those IDs requires an API call. In Pipedream you write that in 10 lines of async Node.js. In Zapier you'd need a separate Lookup step that costs extra tasks and often fails silently on null values. The one scenario where you'd skip Pipedream: if nobody on your team can debug a Node.js error at 9pm when a deal is stuck, use Make instead — the visual debugger is much more forgiving.

Cost

Pipedream's free tier includes 10,000 workflow invocations per month. This workflow uses roughly 1 invocation per deal stage change. A 10-person sales team moving 3-4 deals per day generates about 90 events per month — well inside free. At 50 reps moving 10 deals each per day, you hit 15,000 events/month and need the $19/month Basic plan. Make charges per operation: this workflow takes 3-4 operations per run (webhook receive, API call, filter, Slack post), so 15,000 runs costs about 60,000 operations — roughly $16/month on Make's Core plan. Cost difference is minimal at this scale; pick based on whether you want code or visual UI.

Tradeoffs

Zapier has a pre-built Copper trigger called 'Updated Opportunity' that requires zero setup on the Copper side — no manual webhook registration. That's genuinely faster to start. The problem: Zapier polls every 5-15 minutes on most plans, so your team waits up to 15 minutes for a Closed Won notification. Make offers the same visual simplicity as Zapier with real-time webhook support, which is a better match for this use case than Zapier. n8n lets you self-host and add complex branching logic with its visual node editor — good if you need 10 different Slack routing rules. Pipedream still wins here for one reason: the inline code step handles the stage ID lookup, the null checks, and the Slack formatting in a single place with full debugging output, and it's free up to 10,000 runs.

Three things you'll run into after the first week. First, Copper's webhook doesn't include a 'previous_stage_id' field — you only get the new stage. If you want messages like 'moved FROM Qualified TO Proposal', you need to store stage history yourself, either in Pipedream's built-in data store or an external database. Second, the Copper API rate limit is 600 requests per minute — the stage lookup and assignee lookup together cost 2 API calls per event. At 300 rapid-fire stage changes in one minute (pipeline cleanup scenario), you'll hit the cap and get 429 errors. Add a await new Promise(r => setTimeout(r, 110)) delay between calls as a safety buffer. Third, Copper returns monetary_value as a plain float with no currency symbol or formatting. If your team has international deals, you'll need to store the currency code separately and format it per-locale in your code, or you'll post '$48000' instead of '€40,320' to the wrong channel.

Ideas for what to build next

  • Route notifications to different channels by stageAdd conditional logic in the Node.js code step to post Closed Won deals to #wins, Stalled deals to #at-risk, and everything else to #deals-live. Takes about 10 minutes to add.
  • Add a daily digest of all stage changesCreate a second Pipedream workflow on a scheduled trigger that queries the Copper API for all deals updated in the last 24 hours and posts a summary table to Slack every morning at 9am.
  • Trigger Copper deal updates from Slack reactionsBuild the reverse direction: when a rep adds a specific emoji reaction to a deal notification in Slack, a Pipedream workflow calls the Copper API to move the deal to the next stage automatically.

Related guides

Was this guide helpful?
Slack + Copper overviewPipedream profile →