Intermediate~15 min setupCommunication & CRMVerified April 2026
Slack logo
Zoho CRM logo

How to Send Zoho CRM Follow-up Reminders to Slack with Pipedream

A Pipedream workflow runs on a schedule, queries Zoho CRM for tasks and activities due today, then posts a Slack DM or channel message to the assigned sales rep.

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

Best for

Sales teams using Zoho CRM who want reps to get a Slack nudge each morning — or hour — for tasks due that day without building a custom app.

Not ideal for

Teams needing reminders pushed the instant a task is created in Zoho; for that, use Zoho's own notification rules or a webhook-capable trigger.

Sync type

scheduled

Use case type

notification

Real-World Example

💡

A 12-person SaaS sales team was missing roughly 20% of scheduled follow-up calls per week because reps only saw Zoho task alerts inside the CRM itself — which most ignored after 9am. After setting up this workflow, a Pipedream cron runs every morning at 8am, pulls all tasks due that day from Zoho CRM, and sends each rep a Slack DM with the contact name, company, task subject, and a direct link to the Zoho record. Missed follow-ups dropped to under 5% within the first month.

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.

Zoho CRM account with API access enabled — go to Zoho CRM > Setup > Developer Space > APIs and confirm your org ID and API access is turned on
Zoho OAuth client configured with scopes: ZohoCRM.modules.activities.READ and ZohoCRM.users.READ — Pipedream handles the OAuth flow but your Zoho account must have API permissions
Slack workspace where you have permission to install apps and send DMs — you need the OAuth scopes chat:write, users:read, and users:read.email
Pipedream account (free tier works for under ~333 runs/month; paid plans start at $19/month for higher volume)
Sales reps' work email addresses in Zoho CRM must match their Slack account emails exactly — the Slack user lookup fails if these differ

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Task SubjectSubject
Due DateDue_Date
Task Owner EmailOwner.email
Task StatusStatus
Task Record IDid
4 optional fields▸ show
Contact NameContact_Name
Account NameAccount_Name
Task PriorityPriority
Description / NotesDescription

Step-by-Step Setup

1

pipedream.com > Workflows > New Workflow

Create a new Pipedream workflow

Go to pipedream.com and click 'New Workflow' in the top-right corner of your dashboard. Give the workflow a name like 'Zoho CRM Follow-up Reminders to Slack'. You'll land on the workflow canvas with an empty trigger slot at the top. This is where you'll configure the schedule that drives the whole flow.

  1. 1Log in at pipedream.com
  2. 2Click the blue 'New Workflow' button in the top-right
  3. 3Type a name: 'Zoho CRM Follow-up Reminders to Slack'
  4. 4Click 'Create'
What you should see: You should see the workflow canvas with an empty trigger block labeled 'Add a trigger' at the top.
2

Workflow Canvas > Add a trigger > Schedule

Set a schedule trigger

Click 'Add a trigger' and search for 'Schedule'. Select the 'Schedule' source — this is Pipedream's built-in cron trigger, no app connection required. Set it to run every day at 8:00am in your team's timezone. You can also set it to run hourly if reps need mid-day reminders. Pipedream uses standard cron syntax, so '0 8 * * 1-5' runs Monday through Friday at 8am.

  1. 1Click 'Add a trigger'
  2. 2Search for 'Schedule' and select it
  3. 3Choose 'Run at a custom interval' or 'Cron expression'
  4. 4Enter cron expression: 0 8 * * 1-5 for weekdays at 8am
  5. 5Select your team's timezone from the dropdown
  6. 6Click 'Save and continue'
What you should see: The trigger block should show 'Schedule — runs at 08:00, Mon–Fri' with a green active indicator.
Common mistake — Pipedream runs cron triggers in UTC by default unless you explicitly set a timezone. If you skip the timezone field, reps will get reminders at the wrong hour.
Pipedream
+
click +
search apps
Slack
SL
Slack
Set a schedule trigger
Slack
SL
module added
3

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

Add a Node.js code step to query Zoho CRM

Click '+ Add a step' below the trigger and choose 'Run Node.js code'. This step will call the Zoho CRM REST API to fetch all tasks and activities with a due date of today. You need your Zoho OAuth access token, which you'll handle via a connected account in the next step. For now, add a placeholder — the code will reference the token as auths.zoho_crm.oauth_access_token.

  1. 1Click '+ Add a step' below the Schedule trigger
  2. 2Select 'Run Node.js code'
  3. 3Rename the step to 'fetch_zoho_tasks' by clicking the step title
What you should see: A Node.js code editor should appear with a default async (event, steps) => {} function stub.

This code handles the full workflow: fetching due tasks from Zoho CRM, resolving rep emails to Slack user IDs, and posting Block Kit DMs. Paste step 1 (fetch + Slack lookup) into your 'fetch_zoho_tasks' Node.js step and step 2 (send messages) into your 'send_slack_reminders' step. Replace ZOHO_ORG_ID with your org ID from Zoho CRM > Setup > Developer Space > API.

JavaScript — Code Step// ── Step 1: fetch_zoho_tasks ──────────────────────────────
▸ Show code
// ── Step 1: fetch_zoho_tasks ──────────────────────────────
// Paste this into the fetch_zoho_tasks Node.js step
import axios from 'axios';

... expand to see full code

// ── Step 1: fetch_zoho_tasks ──────────────────────────────
// Paste this into the fetch_zoho_tasks Node.js step
import axios from 'axios';

export default defineComponent({
  props: {
    zoho_crm: {
      type: 'app',
      app: 'zoho_crm',
    },
    slack: {
      type: 'app',
      app: 'slack',
    },
  },
  async run({ steps, $ }) {
    const today = new Date();
    const yyyy = today.getFullYear();
    const mm = String(today.getMonth() + 1).padStart(2, '0');
    const dd = String(today.getDate()).padStart(2, '0');
    const todayStr = `${yyyy}-${mm}-${dd}`;

    // Query Zoho CRM Tasks API for tasks due today that are not completed
    const zohoRes = await axios({
      method: 'GET',
      url: `https://www.zohoapis.com/crm/v3/Tasks`,
      headers: {
        Authorization: `Zoho-oauthtoken ${this.zoho_crm.$auth.oauth_access_token}`,
      },
      params: {
        fields: 'Subject,Due_Date,Status,Priority,Owner,Contact_Name,Account_Name,id,Description',
        criteria: `(Due_Date:equals:${todayStr})and(Status:not_equal:Completed)`,
        per_page: 200,
      },
    });

    // Zoho returns 204 with no body when zero records match
    if (zohoRes.status === 204 || !zohoRes.data || !zohoRes.data.data) {
      await $.flow.exit('No tasks due today — nothing to send.');
    }

    const tasks = zohoRes.data.data;

    // Resolve each task owner's email to a Slack user ID
    const enrichedTasks = await Promise.all(
      tasks.map(async (task) => {
        const ownerEmail = task.Owner?.email;
        let slackUserId = null;

        if (ownerEmail) {
          try {
            const slackRes = await axios({
              method: 'GET',
              url: 'https://slack.com/api/users.lookupByEmail',
              headers: {
                Authorization: `Bearer ${this.slack.$auth.oauth_access_token}`,
              },
              params: { email: ownerEmail },
            });
            if (slackRes.data.ok) {
              slackUserId = slackRes.data.user.id;
            } else {
              console.warn(`Slack lookup failed for ${ownerEmail}: ${slackRes.data.error}`);
            }
          } catch (err) {
            console.warn(`Error resolving Slack user for ${ownerEmail}:`, err.message);
          }
        }

        return { ...task, slackUserId };
      })
    );

    return enrichedTasks;
  },
});

// ── Step 2: send_slack_reminders ─────────────────────────
// Paste this into a separate Node.js step named send_slack_reminders
import axios from 'axios';

const ZOHO_ORG_ID = 'REPLACE_WITH_YOUR_ORG_ID'; // e.g. '4876234000'

export default defineComponent({
  props: {
    slack: {
      type: 'app',
      app: 'slack',
    },
  },
  async run({ steps, $ }) {
    const tasks = steps.fetch_zoho_tasks.$return_value;

    if (!tasks || tasks.length === 0) {
      await $.flow.exit('No enriched tasks to send.');
    }

    const results = [];

    for (const task of tasks) {
      if (!task.slackUserId) {
        console.warn(`Skipping task "${task.Subject}" — no Slack user ID resolved for owner.`);
        continue;
      }

      // Format date from YYYY-MM-DD to MM/DD/YYYY
      const [y, m, d] = task.Due_Date.split('-');
      const formattedDate = `${m}/${d}/${y}`;

      const priorityEmoji = task.Priority === 'High' ? '🔴' : task.Priority === 'Normal' ? '🟡' : '⚪';
      const zohoLink = `https://crm.zoho.com/crm/org${ZOHO_ORG_ID}/tab/Tasks/${task.id}`;

      const message = {
        channel: task.slackUserId,
        blocks: [
          {
            type: 'header',
            text: {
              type: 'plain_text',
              text: `📋 Follow-up due today: ${task.Subject}`,
            },
          },
          {
            type: 'section',
            fields: [
              { type: 'mrkdwn', text: `*Contact:*\n${task.Contact_Name || 'N/A'}` },
              { type: 'mrkdwn', text: `*Account:*\n${task.Account_Name || 'N/A'}` },
              { type: 'mrkdwn', text: `*Due:*\n${formattedDate}` },
              { type: 'mrkdwn', text: `*Priority:*\n${priorityEmoji} ${task.Priority || 'Normal'}` },
            ],
          },
          task.Description
            ? {
                type: 'section',
                text: { type: 'mrkdwn', text: `*Notes:* ${task.Description}` },
              }
            : null,
          {
            type: 'actions',
            elements: [
              {
                type: 'button',
                text: { type: 'plain_text', text: 'View in Zoho CRM' },
                url: zohoLink,
                style: 'primary',
              },
            ],
          },
        ].filter(Boolean),
      };

      const res = await axios({
        method: 'POST',
        url: 'https://slack.com/api/chat.postMessage',
        headers: {
          Authorization: `Bearer ${this.slack.$auth.oauth_access_token}`,
          'Content-Type': 'application/json',
        },
        data: message,
      });

      if (!res.data.ok) {
        console.error(`Failed to send Slack DM for task ${task.id}: ${res.data.error}`);
      } else {
        results.push({ taskId: task.id, slackTs: res.data.ts, status: 'sent' });
      }
    }

    return results;
  },
});
4

Step Panel > Connect an account > Zoho CRM > OAuth

Connect your Zoho CRM account

In the code step, click 'Connect an account' at the top of the step panel and search for 'Zoho CRM'. Click it and follow the OAuth flow — you'll be redirected to Zoho's login page, asked to authorize Pipedream, and then returned to your workflow. Pipedream stores the token securely. Once connected, the token is available inside code steps as auths.zoho_crm.oauth_access_token.

  1. 1Click 'Connect an account' inside the Node.js step
  2. 2Search for 'Zoho CRM' and select it
  3. 3Click 'Connect new account'
  4. 4Log in to Zoho and click 'Accept' on the permissions screen
  5. 5Return to Pipedream — you should see your Zoho account name listed
What you should see: Your Zoho account email appears in the 'Connected account' dropdown with a green checkmark.
Common mistake — Zoho CRM accounts are region-specific — .com, .eu, .in, etc. If your Zoho instance is on a non-.com domain, the OAuth redirect may fail. Check your Zoho datacenter at zoho.com/crm/developer and set the correct base URL in your API calls.
Pipedream settings
Connection
Choose a connection…Add
click Add
Slack
Log in to authorize
Authorize Pipedream
popup window
Connected
green checkmark
5

Workflow Canvas > fetch_zoho_tasks step > Code editor

Write the Zoho CRM API query for due tasks

Paste the Node.js code into the step editor. The code calls Zoho CRM's Activities (Tasks) API filtered by due_date equal to today's date. It returns an array of tasks with fields: subject, due date, assigned user, contact name, and the Zoho record ID for building a deep link. The code handles pagination up to 200 records per run using the page parameter. See the pro tip code block below for the full implementation.

  1. 1Paste the full Node.js code from the pro tip block into the code editor
  2. 2Verify the baseUrl variable matches your Zoho datacenter (default: zohoapis.com)
  3. 3Click 'Test' to run the step and verify tasks are returned
What you should see: The test output panel shows a JSON array of task objects. Each object should include Subject, Due_Date, Owner, Contact_Name, and id.
Common mistake — Zoho CRM's API returns a 204 with no body when there are zero results — not an empty array. Your code must handle this case or the next step will throw a TypeError on a null value.
6

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

Add a step to look up Slack user IDs

Add another Node.js code step after the Zoho fetch. This step takes the task owner's email address from Zoho and resolves it to a Slack user ID using users.lookupByEmail. You need this because Slack DMs require a user ID, not an email. Without this step, you can only post to a shared channel — not send direct messages to individual reps.

  1. 1Click '+ Add a step' below the fetch_zoho_tasks step
  2. 2Select 'Run Node.js code'
  3. 3Rename the step to 'lookup_slack_users'
  4. 4Connect your Slack account via 'Connect an account > Slack'
What you should see: After connecting Slack, your workspace name appears in the connected account dropdown. The step is ready for code.
Common mistake — The `users.lookupByEmail` Slack API call requires the `users:read.email` OAuth scope. If you connected Slack with a bot token that's missing this scope, the call returns a `users_not_found` error even for valid emails. Reconnect with a user token or add the scope to your Slack app.
7

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

Add a Slack step to send the reminder message

Add a third code step named 'send_slack_reminders'. This step iterates over the tasks array from step 1, pairs each task with the resolved Slack user ID from step 2, and calls chat.postMessage for each rep. The message includes the task subject, contact name, due date, and a clickable link to the Zoho record formatted as https://crm.zoho.com/crm/org[your-org-id]/tab/Tasks/[task-id]. Use Slack Block Kit for a clean, scannable layout.

  1. 1Click '+ Add a step' below lookup_slack_users
  2. 2Select 'Run Node.js code'
  3. 3Rename the step to 'send_slack_reminders'
  4. 4Paste the Slack posting code from the pro tip block
  5. 5Replace [your-org-id] with your actual Zoho org ID found under Zoho CRM > Setup > Developer Space > API
What you should see: Test output shows a successful ok: true response from Slack for each message sent, along with the ts timestamp of each message.
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.
message template
🔔 New Record: {{text}} {{user}}
channel: {{channel}}
ts: {{ts}}
#sales
🔔 New Record: Jane Smith
Company: Acme Corp
8

Workflow Canvas > send_slack_reminders step > Code editor

Map Zoho fields to Slack message fields

Open the field mapping section in your send_slack_reminders step. Confirm each Zoho API field name maps to the correct spot in the Slack Block Kit JSON. The Subject field becomes the bold title, Contact_Name maps to the supporting text, Due_Date formats to MM/DD/YYYY for readability, and the record id builds the deep-link URL. Use JavaScript's new Date().toLocaleDateString('en-US') to format the date consistently across time zones.

  1. 1Verify steps.fetch_zoho_tasks.$return_value is referenced correctly
  2. 2Check that Subject, Contact_Name, Due_Date, and id are all mapped
  3. 3Run a test and inspect the Slack message received in your DM
What you should see: You receive a Slack DM with the task subject bolded, the contact name below it, the due date formatted cleanly, and a clickable 'View in Zoho' link.
Common mistake — Zoho returns `Due_Date` as 'YYYY-MM-DD'. If you pass this string directly to Slack, it looks like '2024-03-15' in the message. Format it in JavaScript before sending or reps will find it hard to read at a glance.
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
9

Workflow Canvas > send_slack_reminders step > Code editor (top of function)

Add error handling for empty task lists

Add a conditional check at the top of your send_slack_reminders step. If the tasks array from the Zoho fetch step is empty or undefined, call $.flow.exit('No tasks due today') to stop the workflow cleanly without posting anything to Slack. This prevents empty messages from going out on weekends, holidays, or slow days. Pipedream will show this as a successful run with a custom exit message rather than an error.

  1. 1Open the send_slack_reminders code step
  2. 2Add the null/empty check at line 1 of the async function
  3. 3Use $.flow.exit('No tasks due today') to halt gracefully
  4. 4Test with a date that has no tasks to verify the workflow exits cleanly
What you should see: In the Pipedream event inspector, runs with no tasks show status 'Stopped — No tasks due today' instead of an error or empty Slack message.
10

Workflow Canvas > Test workflow button (top bar)

Test the full workflow end-to-end

Click 'Test workflow' at the top of the canvas. Pipedream will fire the schedule trigger manually and run all steps in sequence. Watch the event inspector on the right — each step shows its input, output, and duration. Confirm the Slack DM arrives within 30 seconds. Check that the Zoho deep link opens the correct task record. If you don't have tasks due today, temporarily create one in Zoho CRM with today's date to validate the full path.

  1. 1Create a test task in Zoho CRM due today assigned to your own email
  2. 2Click 'Test workflow' in Pipedream's top bar
  3. 3Watch each step turn green in the event inspector
  4. 4Check your Slack DMs for the reminder message
  5. 5Click the 'View in Zoho' link to confirm it opens the correct record
What you should see: All three steps show green checkmarks. Your Slack DM contains the task subject, contact name, today's date, and a working Zoho link.
Pipedream
▶ Deploy & test
executed
Slack
Zoho CRM
Zoho CRM
🔔 notification
received
11

Workflow Canvas > Deploy button (top-right) > Events tab

Deploy and activate the workflow

Click 'Deploy' in the top-right corner of the canvas. Pipedream saves and activates the workflow — it will now fire automatically at 8am on your schedule. The workflow status changes from 'Draft' to 'Active'. You can monitor run history under the 'Events' tab. Set up email alerts for workflow errors under Settings > Notifications so you know immediately if the Zoho API call fails.

  1. 1Click the blue 'Deploy' button in the top-right
  2. 2Confirm the workflow status badge changes to 'Active'
  3. 3Go to Settings > Notifications and enable error email alerts
  4. 4Check the Events tab the next morning to confirm the first live run
What you should see: Workflow status shows 'Active'. The Events tab shows the next scheduled run time. Error notifications are enabled.
Common mistake — Free Pipedream accounts pause workflows after 30 days of inactivity. If no tasks exist for 30+ consecutive days, the workflow may stop firing. Upgrade to a paid plan or manually re-enable it if this happens.

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 at least one person comfortable reading Node.js — even just enough to modify a field name. Pipedream gives you direct API access to both Zoho CRM and Slack without fighting a visual editor to handle Zoho's 204 empty response or Slack's Block Kit JSON. The code steps are the feature: you write exactly what you mean, and the platform runs it on schedule. If nobody on your team touches code at all, use Zapier instead — its Zoho CRM 'Search Records' action and Slack 'Send Direct Message' action cover this use case in a no-code UI, and you won't need to handle the empty-response edge case manually.

Cost

Pipedream's free tier gives you 333 workflow executions per month (they call them invocations). This workflow runs once per day on a weekday schedule — that's about 22 runs/month. Each run is one invocation regardless of how many tasks it processes or how many Slack messages it sends. You'll stay well within the free tier indefinitely. At the $19/month paid tier you get 20,000 invocations/month — more than enough even if you add hourly reminders. Compare that to Zapier, where each Slack message sent counts as a separate task: 20 reps × 3 tasks each × 22 days = 1,320 Zapier tasks/month, which hits the $29.99/month plan. Pipedream is cheaper here by about $11/month for the same output.

Tradeoffs

Make's visual router handles conditional Slack formatting better out of the box — you can visually branch 'if priority is High, add red emoji' without writing an if statement. n8n has a native Zoho CRM node that handles the 204 empty response automatically, which removes one of the trickier gotchas in the Pipedream version. Zapier's Zoho CRM integration is more stable and better documented for non-developers. Power Automate has no reliable Zoho CRM connector — skip it entirely for this workflow. Pipedream is still the right call here because the Zoho API has enough quirks (region-specific base URLs, 204 empty responses, nested Owner object) that a code step handles them cleanly in 10 lines where a visual tool needs 4 extra nodes and still might break on edge cases.

Three things you'll hit post-setup. First, Zoho's OAuth token refresh sometimes fails silently on Pipedream after 30+ days — the workflow runs, returns 401, and you don't notice until a rep complains they missed a call. Set up error email alerts on day one. Second, if your Zoho instance is on a regional domain (.eu, .in, .com.au), the API base URL changes — zohoapis.eu, zohoapis.in, etc. The code defaults to zohoapis.com and will return 404 for every request on a non-.com instance. Third, the Zoho CRM API has a rate limit of 5,000 calls per day per org on most paid plans. This workflow uses 1 call per run for tasks plus 1 Slack lookup per task owner. At 20 reps with 5 tasks each, that's 21 API calls per run — nowhere near the limit. But if you add more modules (Calls, Events), the calls stack up fast.

Ideas for what to build next

  • Add overdue task remindersDuplicate the workflow and change the date filter from 'due today' to 'due before today with status Not Started'. Run this at noon to catch tasks reps have already missed — send to a #overdue-followups Slack channel so managers can see the backlog.
  • Post a daily digest to a team channelAdd a second Slack step that groups all due tasks by rep and posts a single summary to #sales-daily-standup instead of — or in addition to — individual DMs. This gives sales managers visibility into the day's follow-up load without pinging every rep in a shared channel.
  • Write reminder outcomes back to Zoho CRMUse Slack's Events API or a separate Pipedream workflow triggered by Slack block interactions to let reps click 'Mark Done' directly in the Slack message, which fires a Zoho CRM API call to update the task status to Completed — closing the loop without reps opening Zoho at all.

Related guides

Was this guide helpful?
Slack + Zoho CRM overviewPipedream profile →