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

How to Sync Slack Members to Copper with Pipedream

Automatically creates a Copper contact when a new member joins a designated Slack channel, keeping your CRM current without manual data entry.

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

Best for

Sales and partnerships teams who onboard external partners or new hires via Slack and need those people logged in Copper immediately.

Not ideal for

Teams adding hundreds of members per day — at that volume, a batch import via CSV or Copper's native Google Workspace sync is more practical.

Sync type

real-time

Use case type

sync

Real-World Example

💡

A 20-person SaaS company invites all new agency partners to a private #partners Slack channel during onboarding. Before this workflow, the sales ops manager spent 30 minutes each Friday manually cross-checking the channel member list against Copper. Now every partner invite fires a Pipedream workflow that creates the Copper contact within seconds, with name, email, and company pre-filled from their Slack profile.

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.

Slack account with admin access to the workspace and ability to install apps (requires the 'Member Joined Channel' Events API scope: member_joined_channel)
Copper account with API access enabled — find your API key at Settings > Integrations > API Keys in Copper
Pipedream account with at least one connected Slack account granted the scopes: users:read, users:read.email, and channels:read
The target Slack channel must exist and the Pipedream Slack app must be a member (for private channels, manually invite it)

Optional

Slack users in the monitored channel must have their email address set and visible — bot users and guest accounts without emails will cause contact creation to fail

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Full Namename
Email Addressemails
6 optional fields▸ show
Job Titletitle
Phone Numberphone_numbers
Company Namecompany_name
Tagstags
Slack User IDcustom_fields
Lead Sourcelead_source

Step-by-Step Setup

1

pipedream.com > Workflows > New Workflow

Create a new Pipedream workflow

Go to pipedream.com and log in. Click 'New Workflow' in the top-left dashboard. You'll land on the workflow canvas with an empty trigger slot at the top. Give the workflow a name like 'Slack → Copper Contact Sync' in the title bar so you can find it later.

  1. 1Click 'New Workflow' in the left sidebar
  2. 2Click the workflow title at the top and rename it to 'Slack → Copper Contact Sync'
  3. 3Click 'Add a trigger' in the first step block
What you should see: You should see the trigger selection panel open on the right side of the canvas.
2

Trigger > Search Apps > Slack > Member Joined Channel

Set the Slack trigger: member joined channel

In the trigger search box, type 'Slack' and select it. Browse the event list and choose 'Member Joined Channel'. This trigger uses Slack's Events API under the hood — Pipedream registers a webhook URL with Slack automatically when you connect your account. You do not need to manually configure a Slack app.

  1. 1Type 'Slack' in the trigger search bar
  2. 2Select 'Slack' from the app results
  3. 3Click 'Member Joined Channel' from the event list
  4. 4Click 'Connect a Slack account' and authorize via OAuth
  5. 5Select the specific Slack channel to monitor from the dropdown (e.g., #partners)
What you should see: You should see a green 'Connected' badge next to your Slack account name, and the channel name you selected should appear in the Channel field.
Common mistake — The 'Member Joined Channel' trigger only fires for public channels and private channels where the Pipedream Slack app has been explicitly invited. For private channels, type '/invite @Pipedream' inside the channel first — otherwise the trigger silently receives no events.
Pipedream
+
click +
search apps
Slack
SL
Slack
Set the Slack trigger: membe…
Slack
SL
module added
3

Trigger > Generate Test Event

Test the trigger with a real event

Click 'Generate Test Event' at the bottom of the trigger panel. Pipedream will prompt you to actually add a member to your selected Slack channel to produce a live payload. Do this in Slack — invite a test user or yourself to the channel. Once the event fires, Pipedream captures the raw JSON and displays it in the trigger output panel.

  1. 1Click 'Generate Test Event'
  2. 2Open Slack and add a member to the monitored channel
  3. 3Return to Pipedream and click 'Refresh' if the event hasn't appeared
  4. 4Expand the event payload to confirm you see 'user', 'channel', and 'event_ts' fields
What you should see: You should see a JSON payload in the trigger output with at minimum a 'user' field containing a Slack user ID like 'U012AB3CD'.
Common mistake — The raw trigger payload only contains a Slack user ID, not a name or email. You must add a separate lookup step to get the full user profile — do not skip step 4 or your Copper contact will be created with no usable data.
Pipedream
▶ Deploy & test
executed
Slack
Copper
Copper
🔔 notification
received
4

Steps > Add Step > Slack > Get User

Add a Slack step to fetch the user's profile

Click the '+' button below the trigger to add a new step. Search for 'Slack' and select the 'Get User' action. This calls the Slack users.info API endpoint using the user ID from the trigger event. Map the user ID field to the trigger output: steps.trigger.event.event.user. This returns the full profile including display name, real name, email, title, and company.

  1. 1Click the '+' button below the trigger block
  2. 2Search for 'Slack' and select it
  3. 3Choose 'Get User' from the action list
  4. 4In the 'User ID' field, click the reference picker and select steps.trigger.event.event.user
  5. 5Click 'Test' to run this step and confirm the profile returns
What you should see: The step output should show a 'user' object with 'real_name', 'profile.email', 'profile.title', 'profile.company', and 'profile.phone' fields populated.
Common mistake — Slack only returns a user's email if your Slack app has the 'users:read.email' OAuth scope. If the email field is missing from the profile output, go to your Slack app settings at api.slack.com/apps and add that scope, then reconnect the account in Pipedream.
5

Steps > Add Step > Run Node.js code

Add a Node.js code step to check for duplicates

Before creating the Copper contact, you need to verify one doesn't already exist with that email. Click '+' and choose 'Run Node.js code'. Write a fetch call to the Copper People Search API using the email from the Slack profile. If Copper returns a non-empty results array, you can either skip creation or update the existing record. This prevents duplicate contacts when someone leaves and rejoins the channel.

  1. 1Click the '+' button below the Get User step
  2. 2Select 'Run Node.js code'
  3. 3Paste the duplicate-check code (see pro tip below)
  4. 4Click 'Test' to run the step and inspect the output
What you should see: The step should return either an existing Copper contact ID (if found) or null, which your next step will use to decide whether to create or skip.
Common mistake — Copper's People Search API matches on exact email. If the Slack user has a different email than what's already in Copper (e.g., personal vs. work), it won't detect the duplicate. There's no fuzzy matching available via the API.

This code handles three things in one step: duplicate checking via Copper's People Search API, email format validation, and graceful exit if the contact already exists. Paste this as a 'Run Node.js code' step between your Slack Get User step and the Copper Create Person step.

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 userProfile = steps.get_user.user.profile;
    const email = userProfile?.email;

    // Guard: no email means we can't create a usable Copper contact
    if (!email) {
      $.flow.exit('Skipping: Slack user has no email address in their profile.');
    }

    const copperEmail = process.env.COPPER_EMAIL;
    const copperApiKey = process.env.COPPER_API_KEY;
    const authHeader = Buffer.from(`${copperEmail}:${copperApiKey}`).toString('base64');

    // Search Copper for an existing contact with this email
    const searchResponse = await axios.post(
      'https://api.copper.com/developer_api/v1/people/search',
      { emails: [email] },
      {
        headers: {
          'X-PW-AccessToken': copperApiKey,
          'X-PW-Application': 'developer_api',
          'X-PW-UserEmail': copperEmail,
          'Content-Type': 'application/json',
        },
      }
    );

    const existingContacts = searchResponse.data;

    if (existingContacts.length > 0) {
      const existing = existingContacts[0];
      console.log(`Duplicate found: Copper Person ID ${existing.id} already has email ${email}`);
      $.flow.exit(`Skipping: Contact already exists in Copper (ID: ${existing.id}).`);
    }

    // Format data for the Copper Create Person step
    $.export('contactPayload', {
      name: userProfile.real_name || steps.get_user.user.name,
      emails: [{ email, category: 'work' }],
      phone_numbers: userProfile.phone
        ? [{ number: userProfile.phone, category: 'work' }]
        : [],
      title: userProfile.title || null,
      company_name: userProfile.company || null,
      tags: ['slack-partners'],
      lead_source: 'Slack',
    });

    console.log(`Ready to create Copper contact for ${email}`);
  },
});
6

Steps > Add Step > Run Node.js code

Add a conditional step to gate contact creation

Click '+' and add another 'Run Node.js code' step. Write a simple conditional that checks whether the previous step returned an existing contact ID. If it did, export a flag like $.export('should_create', false). If it didn't, export $.export('should_create', true). The next step will read this flag to decide whether to proceed.

  1. 1Click '+' to add a new code step
  2. 2Write the conditional check against the duplicate-check step output
  3. 3Use $.export('should_create', true/false) to pass the decision downstream
  4. 4Click 'Test' to confirm the exported value appears in the step output
What you should see: The step output should show 'should_create: true' for a new user and 'should_create: false' for an already-existing Copper contact.
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.
Slack
SL
trigger
filter
Condition
matches criteria?
yes — passes through
no — skipped
Copper
CO
notified
7

Steps > Add Step > Copper > Create Person

Add the Copper 'Create Person' step

Click '+' and search for 'Copper'. Select the 'Create Person' action. Connect your Copper account using your Copper API key and registered email — both are found in Copper under Settings > Integrations > API. Map the fields from the Slack profile step: Name from steps.get_user.user.real_name, Email from steps.get_user.user.profile.email, Title from steps.get_user.user.profile.title, Phone from steps.get_user.user.profile.phone.

  1. 1Click '+' and search for 'Copper'
  2. 2Select 'Create Person'
  3. 3Click 'Connect a Copper account' and enter your API key and email
  4. 4Map 'Name' to steps.get_user.user.real_name
  5. 5Map 'Email' to steps.get_user.user.profile.email
  6. 6Map 'Title' to steps.get_user.user.profile.title
What you should see: After clicking 'Test', you should see a Copper person ID in the step output (e.g., 'id: 892341') and the contact should appear in Copper under People within a few seconds.
Common mistake — Copper's API requires at least one email address to create a person record. If the Slack user has no email in their profile (guests or bots often don't), the step will throw a 400 error. Handle this in your code step by checking for a null email before reaching this step.
8

Steps > Copper > Create Person > tags field

Add a tag to the Copper contact

Immediately after the Create Person step, add another Copper step or extend the create payload to include tags. Tag the contact with the Slack channel name (e.g., 'slack-partners') so your team knows exactly how this contact entered Copper. This is especially useful when you run the same workflow across multiple channels.

  1. 1In the Copper Create Person step, scroll to the optional 'Tags' field
  2. 2Click the reference picker and type the channel name as a static string, e.g., 'slack-partners'
  3. 3Or reference steps.trigger.event.event.channel dynamically if you want the actual channel ID as a tag
What you should see: The Copper contact should show the tag 'slack-partners' on their record when you open it in Copper.
9

Steps > Add Step > Run Node.js code

Add error handling for failed contact creation

Click '+' after the Copper step and add a 'Run Node.js code' step. Wrap the logic in a try/catch and use $.flow.exit() to gracefully stop the workflow if Copper returns an error. Optionally, send a Slack DM to a designated admin using the Slack 'Send Message' action if the creation fails — this gives you visibility without checking Pipedream logs manually.

  1. 1Click '+' to add a code step after the Copper step
  2. 2Check if the Copper step output contains an error property
  3. 3If error exists, call $.flow.exit('Contact creation failed: ' + error.message)
  4. 4Optionally add a Slack step after to DM your admin channel with the failure details
What you should see: If Copper creation fails, the workflow should stop cleanly with an exit message visible in the Pipedream execution log rather than crashing with an unhandled exception.
10

Workflow Canvas > Deploy > Executions tab

Deploy and test with a live channel join

Click 'Deploy' in the top-right corner of the canvas. The workflow is now live and listening for Slack events. Test it by adding a real user to your monitored Slack channel. Switch to Pipedream's Executions tab (left sidebar) and watch for a new execution to appear within seconds. Click into the execution to see each step's input and output.

  1. 1Click the blue 'Deploy' button in the top-right
  2. 2Open Slack and add a test user to the monitored channel
  3. 3Return to Pipedream and click 'Executions' in the left sidebar
  4. 4Click the most recent execution to inspect each step's output
  5. 5Verify the new contact appears in Copper under People
What you should see: The execution should show all steps as green checkmarks. In Copper, the new contact should appear with name, email, title, and the slack-partners tag within 10-15 seconds of the Slack invite.
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.

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 any tolerance for Node.js and wants the contact created in under 10 seconds with zero infrastructure overhead. The key reason Pipedream wins here specifically: the Slack 'Member Joined Channel' webhook fires instantly, and Pipedream processes it without the polling delays you'd get from Make or Zapier's free tiers. The one scenario where you'd pick something else: if everyone on your team is non-technical and needs to maintain this workflow themselves — Zapier's UI is more forgiving for people who've never read a JSON payload.

Cost

Pipedream's free tier gives you 10,000 credits per month. This workflow costs roughly 3-4 credits per execution (trigger + profile lookup + code steps + Copper write). At 50 new channel members per month — a realistic number for a mid-size SaaS company — you're burning around 200 credits. That's 2% of your free monthly allowance. You'd need to be onboarding 2,500+ contacts per month before hitting the free tier ceiling. Zapier charges $49/month for the equivalent Zap at 750 tasks. Make's free tier caps at 1,000 operations/month and would handle the same volume. For this specific use case, Pipedream is either free or dramatically cheaper than Zapier.

Tradeoffs

Make has one genuine edge here: its visual flow canvas makes the conditional duplicate-check logic easier to follow without reading code. Zapier has a pre-built Copper integration with better field mapping UI — you won't need to format email arrays manually. n8n handles the Slack profile enrichment step natively without writing fetch calls, which saves 10 minutes of setup. Power Automate has no meaningful Copper connector, so cross it off the list entirely for this workflow. Despite those advantages elsewhere, Pipedream is still the right call here because the Slack Events API webhook handling is instantaneous, the Node.js step gives you full control over the duplicate logic and email formatting, and you won't pay anything meaningful until you're onboarding at scale.

Three things you'll hit after launch. First: Slack guest accounts (external users invited to a channel) often have no email address in their profile. Your workflow will throw a Copper 422 error unless you check for a null email before the create step. Second: Copper's People Search API only matches exact emails — if a partner uses a different email than the one in Copper, you'll create a duplicate. Store the Slack user ID in a Copper custom field on first creation and check that field on re-joins. Third: if you're monitoring a high-traffic channel and multiple people join within seconds of each other, Pipedream will spawn concurrent executions. Each will independently query Copper before the others finish writing — meaning two executions can both pass the duplicate check and both create a contact for the same person. Add a small delay step (500ms) and accept that true race conditions at this scale require a dedicated deduplication table outside Copper.

Ideas for what to build next

  • Sync Copper Updates Back to SlackWhen a Copper contact's status changes (e.g., marked as a Customer), post an automated message to the original Slack channel. This closes the loop between your CRM and the team managing the relationship.
  • Enrich Contacts with Clearbit After CreationAdd a Clearbit Enrichment step in Pipedream immediately after the Copper Create Person step. Use the email to pull company size, industry, LinkedIn URL, and funding stage, then update the Copper contact with those fields automatically.
  • Monitor Multiple Channels with One WorkflowExtend the workflow to watch several Slack channels (e.g., #partners, #clients, #vendors) and tag each Copper contact with the specific channel name they joined. Use a Pipedream filter step to route contacts to different Copper pipelines based on channel.

Related guides

Was this guide helpful?
Slack + Copper overviewPipedream profile →