

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
scheduledUse case type
notificationReal-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.
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
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.
Field Mapping
Map these fields between your apps.
| Field | API Name | |
|---|---|---|
| Required | ||
| Task Subject | Subject | |
| Due Date | Due_Date | |
| Task Owner Email | Owner.email | |
| Task Status | Status | |
| Task Record ID | id | |
4 optional fields▸ show
| Contact Name | Contact_Name |
| Account Name | Account_Name |
| Task Priority | Priority |
| Description / Notes | Description |
Step-by-Step Setup
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.
- 1Log in at pipedream.com
- 2Click the blue 'New Workflow' button in the top-right
- 3Type a name: 'Zoho CRM Follow-up Reminders to Slack'
- 4Click 'Create'
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.
- 1Click 'Add a trigger'
- 2Search for 'Schedule' and select it
- 3Choose 'Run at a custom interval' or 'Cron expression'
- 4Enter cron expression: 0 8 * * 1-5 for weekdays at 8am
- 5Select your team's timezone from the dropdown
- 6Click 'Save and continue'
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.
- 1Click '+ Add a step' below the Schedule trigger
- 2Select 'Run Node.js code'
- 3Rename the step to 'fetch_zoho_tasks' by clicking the step title
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;
},
});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.
- 1Click 'Connect an account' inside the Node.js step
- 2Search for 'Zoho CRM' and select it
- 3Click 'Connect new account'
- 4Log in to Zoho and click 'Accept' on the permissions screen
- 5Return to Pipedream — you should see your Zoho account name listed
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.
- 1Paste the full Node.js code from the pro tip block into the code editor
- 2Verify the
baseUrlvariable matches your Zoho datacenter (default: zohoapis.com) - 3Click 'Test' to run the step and verify tasks are returned
Subject, Due_Date, Owner, Contact_Name, and id.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.
- 1Click '+ Add a step' below the fetch_zoho_tasks step
- 2Select 'Run Node.js code'
- 3Rename the step to 'lookup_slack_users'
- 4Connect your Slack account via 'Connect an account > Slack'
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.
- 1Click '+ Add a step' below lookup_slack_users
- 2Select 'Run Node.js code'
- 3Rename the step to 'send_slack_reminders'
- 4Paste the Slack posting code from the pro tip block
- 5Replace [your-org-id] with your actual Zoho org ID found under Zoho CRM > Setup > Developer Space > API
ok: true response from Slack for each message sent, along with the ts timestamp of each message.channel: {{channel}}
ts: {{ts}}
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.
- 1Verify steps.fetch_zoho_tasks.$return_value is referenced correctly
- 2Check that Subject, Contact_Name, Due_Date, and id are all mapped
- 3Run a test and inspect the Slack message received in your DM
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.
- 1Open the send_slack_reminders code step
- 2Add the null/empty check at line 1 of the async function
- 3Use $.flow.exit('No tasks due today') to halt gracefully
- 4Test with a date that has no tasks to verify the workflow exits cleanly
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.
- 1Create a test task in Zoho CRM due today assigned to your own email
- 2Click 'Test workflow' in Pipedream's top bar
- 3Watch each step turn green in the event inspector
- 4Check your Slack DMs for the reminder message
- 5Click the 'View in Zoho' link to confirm it opens the correct record
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.
- 1Click the blue 'Deploy' button in the top-right
- 2Confirm the workflow status badge changes to 'Active'
- 3Go to Settings > Notifications and enable error email alerts
- 4Check the Events tab the next morning to confirm the first live run
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
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.
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.
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 reminders — Duplicate 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 channel — Add 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 CRM — Use 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
How to Share Notion Meeting Notes to Slack with Pipedream
~15 min setup
How to Share Notion Meeting Notes to Slack with Power Automate
~15 min setup
How to Share Notion Meeting Notes to Slack with n8n
~20 min setup
How to Send Notion Meeting Notes to Slack with Zapier
~8 min setup
How to Share Notion Meeting Notes to Slack with Make
~12 min setup
How to Create Notion Tasks from Slack with Pipedream
~15 min setup