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

How to Create Copper Tasks from Slack with Pipedream

Listens for a Slack slash command or emoji reaction and instantly creates a follow-up task in Copper CRM, pre-filled with the prospect name, due date, and assignee parsed from the message.

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 sales teams using Copper and Slack together who want to log follow-up tasks without leaving the conversation thread

Not ideal for

Teams that need two-way sync between Slack threads and Copper task updates — use a dedicated Copper webhook listener for that instead

Sync type

real-time

Use case type

import

Real-World Example

💡

A 12-person SaaS sales team at a seed-stage company holds all prospect discussions in Slack. Before this workflow, a rep had to copy context from Slack, open Copper, find the right contact, and manually create a task — taking 3-4 minutes per task and often skipped entirely. Now a rep types /copper-task follow up with Acme Corp by Friday @jess in any channel, and a task appears in Copper within 5 seconds, assigned to the right person with a parsed due date.

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 — found under Copper Settings > Integrations > API Keys. Generate and copy your API key before starting.
Slack workspace admin access to create a Slack app and install a slash command at api.slack.com/apps
Slack app with OAuth scopes: commands (for slash commands), chat:write (to post confirmations), and users:read.email (to resolve @mentions to email addresses)
Pipedream account — free tier works for low volume (under ~100 tasks/month). No credit card needed to start.
Copper users must have the same email address as their Slack account for the assignee resolution in step 7 to match correctly

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Task Namename
Due Datedue_date
Statusstatus
5 optional fields▸ show
Assignee IDassignee_id
Related Resource Typerelated_resource.type
Related Resource IDrelated_resource.id
Prioritypriority
Detailsdetails

Step-by-Step Setup

1

pipedream.com > Workflows > New Workflow

Create a new Pipedream Workflow

Go to pipedream.com and sign in. Click the green 'New Workflow' button in the top-left of the dashboard. Give the workflow a clear name like 'Slack → Copper: Follow-up Tasks' so it's easy to find later. Pipedream opens a blank canvas with a trigger slot at the top.

  1. 1Click 'New Workflow' in the left sidebar
  2. 2Enter the workflow name 'Slack → Copper: Follow-up Tasks'
  3. 3Click 'Create' to open the workflow editor
What you should see: You should see an empty workflow canvas with a grey 'Add Trigger' block at the top of the steps chain.
2

Workflow Editor > Add Trigger > Slack > Slash Command

Add a Slack Slash Command trigger

Click the grey 'Add Trigger' block. Search for 'Slack' in the app search bar and select it. From the trigger list, choose 'Slash Command' — this fires every time a user invokes your custom slash command in any channel. Pipedream will generate a unique webhook URL that Slack will POST to when the command is used. Copy this URL; you'll need it in step 3.

  1. 1Click the 'Add Trigger' block
  2. 2Type 'Slack' in the search field and select the Slack app
  3. 3Scroll to find 'Slash Command' and click it
  4. 4Copy the generated webhook URL shown in the trigger config panel
What you should see: You should see a trigger block labeled 'Slack — Slash Command' with a unique HTTPS webhook URL displayed below the trigger name.
Common mistake — Pipedream generates a new webhook URL each time you create this trigger. If you ever delete and recreate the trigger, the URL changes and you must update the Slack app config again.
Pipedream
+
click +
search apps
Slack
SL
Slack
Add a Slack Slash Command tr…
Slack
SL
module added
3

api.slack.com/apps > Your App > Slash Commands > Create New Command

Register the Slash Command in your Slack App

Go to api.slack.com/apps and open your existing Slack app, or click 'Create New App' if you don't have one. Navigate to 'Slash Commands' in the left sidebar and click 'Create New Command'. Set the command to /copper-task, paste the Pipedream webhook URL into the 'Request URL' field, and add a short description like 'Create a follow-up task in Copper'. Save the command, then reinstall the app to your workspace from 'Install App' in the left nav.

  1. 1Open api.slack.com/apps and select your Slack app
  2. 2Click 'Slash Commands' in the left sidebar
  3. 3Click 'Create New Command'
  4. 4Set Command to '/copper-task', paste the Pipedream webhook URL into 'Request URL'
  5. 5Click 'Save', then go to 'Install App' and click 'Reinstall to Workspace'
What you should see: The slash command appears in the Slash Commands list with a green checkmark. After reinstalling, /copper-task should autocomplete in any Slack channel.
Common mistake — Slack requires the Request URL to respond with HTTP 200 within 3 seconds. If your Pipedream workflow takes longer to process, Slack shows the user an error even if the task gets created. Keep heavy processing in async steps after the initial response.
4

Workflow Editor > + Add Step > Run Node.js code

Add a step to immediately acknowledge Slack

Slack will time out if it doesn't receive a 200 response within 3 seconds. Add a new step in Pipedream right after the trigger by clicking the '+' button. Select 'Run Node.js code'. Use $.respond() to immediately return a 200 acknowledgment to Slack before the rest of the workflow runs. This step must be the first step after the trigger.

  1. 1Click the '+' button below the Slack trigger block
  2. 2Select 'Run Node.js code' from the step menu
  3. 3Paste the acknowledgment code into the code editor
  4. 4Name the step 'ack_slack'
What you should see: The step block appears labeled 'ack_slack' and the code editor shows your $.respond() call. When you test, Pipedream returns 200 immediately to the Slack request.
Common mistake — $.respond() only works on HTTP-triggered workflows. If you ever switch the trigger type, this step will throw an error and you must remove it.
5

Workflow Editor > + Add Step > Run Node.js code

Parse the slash command text

The Slack slash command payload arrives as a URL-encoded body. Pipedream's trigger automatically parses it into steps.trigger.event.body. Add another Node.js code step to extract the task name, due date, and assignee from the command text. For example, /copper-task follow up with Acme Corp by Friday @jess sends text='follow up with Acme Corp by Friday @jess'. Write a simple parser to split on 'by' for the date and '@' for the user mention.

  1. 1Click '+' below the ack_slack step
  2. 2Select 'Run Node.js code'
  3. 3Name the step 'parse_command'
  4. 4Paste the parsing code that reads steps.trigger.event.body.text
What you should see: After clicking 'Test', the step output panel shows parsed fields: taskName, dueDate, and assigneeMention as separate exportable values.
6

Workflow Editor > + Add Step > Run Node.js code

Look up the Copper contact by name

Copper tasks must be linked to a resource — typically a Person or Opportunity. Add a Node.js code step that calls the Copper People Search API (POST /v1/people/search) with the company or person name extracted from the command text. This returns the Copper person ID you'll attach to the task. Connect your Copper account via 'Connect an Account' in the step config panel — Pipedream stores the API key securely.

  1. 1Click '+' below the parse_command step
  2. 2Select 'Run Node.js code'
  3. 3Name the step 'lookup_copper_contact'
  4. 4Click 'Connect an Account' and select or add your Copper credentials
  5. 5Paste the Copper People Search API call code
What you should see: The step output shows a copper_person_id integer (e.g. 8472910) and the matched contact's full name. If no match is found, the output shows null for both fields.
Common mistake — Copper's People Search is fuzzy but not perfect. If your team uses nicknames or abbreviations in Slack (e.g. 'Acme' instead of 'Acme Corporation'), the lookup may return no results. Add a fallback that creates the task without a linked contact rather than failing silently.
7

Workflow Editor > + Add Step > Run Node.js code

Resolve the Slack @mention to a Copper assignee

The @mention in the slash command gives you a Slack user ID, not a name or email. Add a Node.js step that calls the Slack users.info API to get the user's email address, then queries Copper's /v1/users endpoint to find the matching Copper user ID. This ID goes into the task's assignee_id field. Without this step, all tasks would be assigned to the API key owner by default.

  1. 1Click '+' below the lookup_copper_contact step
  2. 2Select 'Run Node.js code'
  3. 3Name the step 'resolve_assignee'
  4. 4Paste code that calls Slack users.info then matches email against Copper /v1/users
What you should see: Step output shows copper_assignee_id as an integer and the matched user's full name (e.g. 27394 / Jessica Tran). If no match, it falls back to null.
Common mistake — Slack users.info requires the users:read.email OAuth scope. If your Slack app was created without it, the API call returns a missing_scope error. Add the scope in api.slack.com/apps > OAuth & Permissions > Scopes, then reinstall.

Paste this as a single Node.js code step in Pipedream — it handles Slack's immediate acknowledgment, command parsing, Copper contact lookup, assignee resolution, task creation, and Slack confirmation in one step with full error handling. Place it directly after the Slack Slash Command trigger and name the step 'full_task_pipeline'.

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 body = steps.trigger.event.body;
    const rawText = body.text || '';
    const channelId = body.channel_id;
    const slackUserId = body.user_id;

    // Immediately acknowledge Slack to avoid timeout
    await $.respond({ status: 200, body: '' });

    // --- Parse slash command text ---
    // Format: "task description by [date] @mention"
    const byIndex = rawText.toLowerCase().lastIndexOf(' by ');
    const atIndex = rawText.lastIndexOf('@');

    const taskName = byIndex > -1
      ? rawText.substring(0, byIndex).trim()
      : rawText.replace(/@\S+/, '').trim();

    const dateStr = byIndex > -1
      ? rawText.substring(byIndex + 4, atIndex > byIndex ? atIndex : undefined).trim()
      : null;

    const mentionUsername = atIndex > -1
      ? rawText.substring(atIndex + 1).trim().split(' ')[0]
      : null;

    // Convert natural date to Unix timestamp (seconds)
    const dueTimestamp = dateStr
      ? Math.floor(new Date(dateStr).getTime() / 1000)
      : Math.floor(Date.now() / 1000) + 86400 * 3; // default: 3 days from now

    const copperHeaders = {
      'X-PW-AccessToken': process.env.COPPER_API_KEY,
      'X-PW-Application': 'developer_api',
      'X-PW-UserEmail': process.env.COPPER_USER_EMAIL,
      'Content-Type': 'application/json',
    };

    // --- Resolve Slack @mention to Copper assignee ID via email ---
    let copperAssigneeId = null;
    if (mentionUsername) {
      try {
        const slackUsersResp = await axios.get(
          `https://slack.com/api/users.lookupByEmail?email=${mentionUsername}@${process.env.COMPANY_EMAIL_DOMAIN}`,
          { headers: { Authorization: `Bearer ${process.env.SLACK_BOT_TOKEN}` } }
        );
        const slackEmail = slackUsersResp.data?.user?.profile?.email;
        if (slackEmail) {
          const copperUsersResp = await axios.get(
            'https://api.copper.com/developer_api/v1/users',
            { headers: copperHeaders }
          );
          const matched = copperUsersResp.data.find(u => u.email === slackEmail);
          if (matched) copperAssigneeId = matched.id;
        }
      } catch (e) {
        console.log('Assignee resolution failed, proceeding without assignee:', e.message);
      }
    }

    // --- Search for Copper contact by name ---
    let relatedResource = null;
    try {
      const searchResp = await axios.post(
        'https://api.copper.com/developer_api/v1/people/search',
        { name: taskName.replace(/^(urgent:|follow up with|check in with)/i, '').trim() },
        { headers: copperHeaders }
      );
      if (searchResp.data.length > 0) {
        relatedResource = { id: searchResp.data[0].id, type: 'person' };
      }
    } catch (e) {
      console.log('Contact lookup failed:', e.message);
    }

    // --- Create Copper task ---
    let copperTask = null;
    try {
      const taskPayload = {
        name: taskName,
        due_date: dueTimestamp,
        status: 'Open',
        details: `Created from Slack #${body.channel_name}: ${rawText}`,
        ...(copperAssigneeId && { assignee_id: copperAssigneeId }),
        ...(relatedResource && { related_resource: relatedResource }),
      };

      const taskResp = await axios.post(
        'https://api.copper.com/developer_api/v1/tasks',
        taskPayload,
        { headers: copperHeaders }
      );
      copperTask = taskResp.data;
    } catch (e) {
      // Notify user of failure via Slack DM
      await axios.post(
        'https://slack.com/api/chat.postMessage',
        {
          channel: slackUserId,
          text: `❌ Couldn't create Copper task for: "${rawText}"\nCreate it manually: https://app.copper.com/companies/tasks/new`,
        },
        { headers: { Authorization: `Bearer ${process.env.SLACK_BOT_TOKEN}` } }
      );
      throw new Error(`Copper task creation failed: ${e.message}`);
    }

    // --- Send Slack confirmation ---
    await axios.post(
      'https://slack.com/api/chat.postMessage',
      {
        channel: channelId,
        text: [
          `✅ *Task created in Copper*`,
          `*Task:* ${copperTask.name}`,
          `*Due:* ${new Date(copperTask.due_date * 1000).toDateString()}`,
          `*Assigned to:* ${copperAssigneeId ? mentionUsername : 'Unassigned'}`,
          relatedResource ? `*Linked contact:* Copper Person #${relatedResource.id}` : '',
          `<https://app.copper.com/companies/tasks/${copperTask.id}|View task in Copper>`,
        ].filter(Boolean).join('\n'),
      },
      { headers: { Authorization: `Bearer ${process.env.SLACK_BOT_TOKEN}` } }
    );

    return { copperTaskId: copperTask.id, taskName: copperTask.name, assigneeId: copperAssigneeId };
  },
});
8

Workflow Editor > + Add Step > Run Node.js code

Create the task in Copper

Add a final Node.js code step that calls the Copper Tasks API (POST /v1/tasks). Map the parsed values into the request body: name from the command text, due_date as a Unix timestamp, assignee_id from the resolved user, and related_resource linking to the Copper person found in step 6. The Copper API returns the full created task object including its ID.

  1. 1Click '+' below the resolve_assignee step
  2. 2Select 'Run Node.js code'
  3. 3Name the step 'create_copper_task'
  4. 4Map steps.parse_command.taskName, steps.parse_command.dueDate, steps.resolve_assignee.copper_assignee_id, and steps.lookup_copper_contact.copper_person_id into the POST body
What you should see: The step output shows the full Copper task object with an id field (e.g. 4819203), status 'Open', and the correct name, due date, and assignee populated.
Common mistake — Copper's due_date field expects a Unix timestamp in seconds, not milliseconds. JavaScript's Date.now() returns milliseconds. Divide by 1000 and use Math.floor() before sending, or Copper silently stores a date in the year 52000.
9

Workflow Editor > + Add Step > Run Node.js code

Send a confirmation back to the Slack user

After the task is created, post a confirmation back to the channel or DM the user who triggered the command. Add a Node.js code step that calls the Slack chat.postMessage API with the task details: task name, due date, assignee, and a link to the Copper task using the format https://app.copper.com/companies/tasks/{task_id}. Use the channel_id from the trigger payload as the target channel.

  1. 1Click '+' below the create_copper_task step
  2. 2Select 'Run Node.js code'
  3. 3Name the step 'notify_slack'
  4. 4Use steps.trigger.event.body.channel_id as the Slack channel target
  5. 5Build the message string using steps.create_copper_task.$return_value.id for the Copper link
What you should see: Within 2-3 seconds of the slash command, a bot message appears in the originating Slack channel: '✅ Task created: Follow up with Acme Corp by Friday — assigned to Jessica Tran. View in Copper: [link]'
10

Workflow Editor > + Add Step > Run Node.js code

Add error handling for failed lookups

If the Copper contact lookup or assignee resolution fails, the workflow should not silently drop the task. Add a Node.js step with try/catch that catches errors from steps 6 and 7, then posts a fallback Slack DM to the command sender with a direct link to create the task manually in Copper. Use steps.trigger.event.body.user_id to target the DM. This prevents tasks from being lost when a contact name doesn't match.

  1. 1Click '+' after the notify_slack step, or wrap steps 6-9 in a single try/catch step
  2. 2Name the step 'error_handler'
  3. 3Use $.send.http or the Slack chat.postMessage API to DM the user on failure
  4. 4Include the original command text in the DM so the rep has context to fix it manually
What you should see: When you test with a contact name that doesn't exist in Copper, the workflow sends a Slack DM to the triggering user instead of failing silently, with the message 'Couldn't find a Copper contact for [name]. Create task manually: [link]'
11

Workflow Editor > Deploy button (top right) > Event Inspector (bottom panel)

Deploy and test end-to-end

Click 'Deploy' in the top-right corner of the Pipedream workflow editor to make the workflow live. Open any Slack channel and type /copper-task follow up with Acme Corp by Friday @jess. Watch the Pipedream event inspector (bottom of the workflow editor) to see each step execute in real time. Confirm the task appears in Copper under the matched contact within 5 seconds.

  1. 1Click the blue 'Deploy' button in the top-right corner
  2. 2Open a Slack channel and type '/copper-task follow up with Acme Corp by Friday @jess'
  3. 3Watch the Pipedream Event Inspector update as each step fires
  4. 4Open Copper and navigate to the matched contact to confirm the task appears
What you should see: The Event Inspector shows all steps with green checkmarks, the Slack channel shows the confirmation message, and the Copper contact has a new open task with the correct name, due date, and assignee.
Common mistake — Pipedream's free tier pauses workflows after 30 seconds of total execution per event. If your Copper contact database is large and the search API is slow, steps may time out. Test with a known contact first before going to production.
Pipedream
▶ Deploy & test
executed
Slack
Copper
Copper
🔔 notification
received

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 includes developers who want to customize the task parsing logic, or if you need the slash command to respond in under 500ms (Pipedream's webhook latency averages 150-200ms). The instant webhook processing and Node.js code steps mean you can write a real date parser, handle edge cases in the command text, and call multiple APIs in sequence without stitching together visual blocks. If your team is non-technical and wants to build this without code, use Zapier's Slack trigger with a Code by Zapier step — it's slower to process but easier to hand off to a non-developer to maintain.

Cost

Pipedream's free tier gives you 10,000 credits/month. This workflow consumes roughly 4-6 credits per run (one trigger + four API call steps). At 100 tasks/month, you're using 400-600 credits — well inside free tier. At 1,000 tasks/month you're at 4,000-6,000 credits, still free. The paid Basic plan at $19/month gives 100,000 credits, which handles ~16,000-25,000 tasks/month. Zapier charges per task: at 1,000 tasks/month you'd be on the $29/month Starter plan. Pipedream is cheaper by $10/month at that volume.

Tradeoffs

Zapier has a pre-built Copper integration with a 'Create Task' action that maps fields visually — no code, 10 minutes to set up, but you lose the ability to parse natural language from the slash command text without a Code by Zapier step. Make handles the webhook response timing well with its HTTP module and can parse the command text using its built-in string functions, but the Copper integration is community-built and less maintained. n8n gives you full code control similar to Pipedream and self-hosted options for teams with data residency requirements — if that's your constraint, n8n is the better pick. Power Automate has no native Copper connector, so you'd be building raw HTTP actions for every API call, which is more work than Pipedream with no advantage. Pipedream wins here because the combination of instant webhooks and Node.js steps solves the hardest part of this workflow: responding to Slack in time while still doing real processing.

Three things you'll hit after launch. First, Copper's People Search returns multiple results when a contact name is ambiguous — if you have 'Acme Corp (East)' and 'Acme Corp (West)', the search returns both and your code picks the first one. Add a step that handles multiple results by posting a Slack message asking the user to clarify. Second, Slack's slash command payload encodes spaces as '+' in the text field — your parser must decode the URL encoding before splitting on keywords, or 'follow+up+with+Acme+Corp' will fail to parse. Use decodeURIComponent(body.text.replace(/\+/g, ' ')) before any string operations. Third, Copper rate-limits API calls to 600 requests per minute. At normal usage this isn't an issue, but if you build a batch digest workflow later (next steps above), a team of 20 reps all checking tasks at 8am simultaneously could briefly hit that limit — add a 100ms delay between Copper API calls in batch scenarios.

Ideas for what to build next

  • Add emoji-reaction triggersSet up a second Pipedream workflow that listens for a specific emoji reaction (e.g. 📋) on any Slack message and creates a Copper task from the message text — no slash command needed, works directly on existing prospect discussions.
  • Sync Copper task completions back to SlackBuild a reverse workflow using Copper's webhook for task status changes that posts a '✅ Task completed' message to the originating Slack channel when a rep marks the task done in Copper.
  • Add a daily task digest in SlackCreate a scheduled Pipedream workflow that runs every morning at 8am, queries Copper's tasks API for all tasks due today, and posts a grouped digest to the #sales channel with assignee mentions so reps see their day's follow-ups without opening Copper.

Related guides

Was this guide helpful?
Slack + Copper overviewPipedream profile →