Intermediate~20 min setupCommunication & CRMVerified April 2026
Slack logo
Close logo

How to Send Daily Close Pipeline Updates to Slack with n8n

Every morning at a set time, n8n pulls pipeline metrics, overdue tasks, and upcoming activities from Close CRM and posts a formatted summary to your team's Slack channel.

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

Best for

Sales managers at 5-30 person inside sales teams who want reps to see pipeline health and task deadlines without logging into Close every morning.

Not ideal for

Teams that need real-time deal alerts as they happen — use a webhook-triggered workflow for that instead.

Sync type

scheduled

Use case type

reporting

Real-World Example

💡

A 12-person SaaS sales team sends a 9 AM summary to #pipeline-daily showing each rep's open opportunities, overdue calls, and deals closing this week. Before this workflow, managers ran the same Close search manually each morning, copy-pasted numbers into Slack, and still missed overdue tasks half the time. Now the message is waiting in Slack before standup starts.

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 n8n

Copy the pre-built n8n blueprint and paste it straight into n8n. 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.

Close API key with read access to opportunities, tasks, and users — generated under Close Settings > API Keys
Slack Bot Token (xoxb-) with the chat:write scope and the bot invited to your target channel
n8n instance running version 1.0 or later — either self-hosted or n8n Cloud
Admin or manager-level Close account to access all reps' opportunities and tasks (not just your own)
The Slack channel ID (not display name) for the channel where summaries should post — find it by right-clicking the channel > View channel details

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Opportunity Valuevalue
Opportunity Status Labelstatus_label
Assigned User IDuser_id
Lead Namelead_name
Task Due Datedue_date
Task Assigned Toassigned_to
User Display Namedisplay_name
Opportunity Count per Rep
2 optional fields▸ show
Expected Close Datedate_won
Task Texttext

Step-by-Step Setup

1

n8n Canvas > + Node > Schedule Trigger

Create a new n8n workflow with a Schedule trigger

Open n8n and click the orange 'New Workflow' button in the top right. In the canvas that appears, click the '+' node and search for 'Schedule Trigger'. This node controls when your pipeline summary fires — you will set it to run once per day. Click on the Schedule Trigger node to open its settings panel on the right.

  1. 1Click 'New Workflow' in the top right of the n8n dashboard
  2. 2Click the '+' icon in the center of the blank canvas
  3. 3Type 'Schedule' in the search bar and select 'Schedule Trigger'
  4. 4In the right panel, set 'Trigger Interval' to 'Days'
  5. 5Set 'Hour' to 8 and 'Minute' to 0 to fire at 8:00 AM server time
What you should see: You should see a Schedule Trigger node on the canvas with a label showing 'Every day at 08:00'.
Common mistake — n8n runs on server time, not your local timezone. If your server is UTC and your team is in US Eastern, set the hour to 13 to hit 8 AM EST, or 14 for EDT. Check your n8n instance timezone under Settings > General before proceeding.
n8n
+
click +
search apps
Slack
SL
Slack
Create a new n8n workflow wi…
Slack
SL
module added
2

n8n Sidebar > Settings > Credentials > New Credential > Close.io API

Add your Close API credentials to n8n

Before you can pull data from Close, n8n needs your API key. Go to Settings > Credentials in the n8n sidebar. Click 'New Credential', search for 'Close.io', and paste in your Close API key. Close generates API keys per user — log into Close, go to Settings > API Keys, and create a new key with read access.

  1. 1Open the left sidebar in n8n and click 'Settings'
  2. 2Click 'Credentials' then 'New Credential'
  3. 3Search for 'Close.io' and select the Close.io API credential type
  4. 4Paste your Close API key into the 'API Key' field
  5. 5Click 'Save' and confirm the green 'Connection tested successfully' message appears
What you should see: You should see a green 'Connection tested successfully' banner and a new credential entry named 'Close.io account' in your credentials list.
Common mistake — Close API keys are tied to the user who created them. If that user is deactivated later, the workflow silently breaks. Create the key under a shared service account or a manager-level user who won't be offboarded.
3

n8n Canvas > + Node > HTTP Request

Add an HTTP Request node to pull open opportunities from Close

n8n has a native Close.io node, but it has limited query support for pipeline reporting. Use the HTTP Request node instead — it gives you access to Close's full search API. Add an HTTP Request node connected to the Schedule Trigger. Set the method to GET and the URL to Close's opportunity search endpoint. You will filter for open opportunities in the current period.

  1. 1Click the '+' button to the right of the Schedule Trigger node
  2. 2Search for 'HTTP Request' and select it
  3. 3Set 'Method' to GET
  4. 4Set 'URL' to https://api.close.com/api/v1/opportunity/?status_type=active&_order_by=date_updated&_limit=100
  5. 5Under 'Authentication', select 'Basic Auth', enter your Close API key as the username, and leave the password blank
  6. 6Click 'Execute Node' to test — you should see raw JSON with opportunity records
What you should see: The node output panel shows a JSON array under 'data' containing your active opportunities with fields like id, value, lead_name, status_label, user_name, and date_updated.
Common mistake — Close's default API page size is 100 records. If your team has more than 100 open opportunities, you will only see the first page. Add pagination logic in a later Code node, or filter by user or date to keep results under 100 per run.
4

n8n Canvas > + Node (from Schedule Trigger) > HTTP Request

Add a second HTTP Request node to pull overdue tasks

Connect a second HTTP Request node to the Schedule Trigger in parallel — this one fetches tasks that are past due. In Close, tasks are queried via the task endpoint with a date filter. You want tasks where the due date is before today and the status is not complete. This gives you the overdue activity count per rep.

  1. 1Click the Schedule Trigger node, then click the '+' icon that appears below it to add a parallel branch
  2. 2Add a second HTTP Request node
  3. 3Set 'Method' to GET
  4. 4Set 'URL' to https://api.close.com/api/v1/task/?is_complete=false&_due_date__lt={{ $now.toISO().slice(0,10) }}&_limit=200
  5. 5Apply the same Basic Auth credentials as the previous node
  6. 6Click 'Execute Node' — you should see overdue task records with fields including assigned_to, due_date, lead_name, and text
What you should see: The node output shows a list of task objects. Each task includes assigned_to (a user ID), due_date, and lead_name. You will resolve user IDs to names in the Code node.
Common mistake — The assigned_to field returns a Close user ID like 'user_abc123', not a display name. Do not pass this directly to Slack — the Code node in step 6 handles the lookup. Skipping the lookup produces messages like 'Overdue: user_abc123 has 3 tasks', which is useless.
5

n8n Canvas > + Node (from Schedule Trigger) > HTTP Request

Add an HTTP Request node to fetch Close users for name resolution

To show rep names in Slack instead of user IDs, you need a list of Close users and their IDs. Add a third HTTP Request node, also connected to the Schedule Trigger in parallel. This call returns your organization's users with their id and display_name fields. The Code node merges this list with the task and opportunity data.

  1. 1Add a third HTTP Request node from the Schedule Trigger
  2. 2Set 'Method' to GET
  3. 3Set 'URL' to https://api.close.com/api/v1/me/ — first confirm your own user, then use https://api.close.com/api/v1/organization/ to get the org ID
  4. 4For full user list, set URL to https://api.close.com/api/v1/user/?_limit=100
  5. 5Apply the same Basic Auth credentials
  6. 6Execute the node and confirm you see user objects with id and display_name fields
What you should see: The node returns a list of user objects. Each has an id like 'user_abc123' and a display_name like 'Sarah Okonkwo'. You will use this as a lookup table in the Code node.
6

n8n Canvas > + Node > Merge > Mode: Merge by Position

Add a Merge node to combine all three API responses

Now that you have three parallel data streams — opportunities, tasks, and users — you need to combine them before processing. Add a Merge node to the canvas. Connect all three HTTP Request nodes into the Merge node. Set the Merge mode to 'Merge by Position' — this passes all three datasets through as separate input arrays that the Code node can reference by index.

  1. 1Click the '+' on any empty canvas area and search for 'Merge'
  2. 2Drag the output of the Opportunities HTTP Request node to the first input of the Merge node
  3. 3Drag the output of the Tasks HTTP Request node to the second input
  4. 4Drag the output of the Users HTTP Request node to the third input
  5. 5Set 'Mode' to 'Merge by Position'
  6. 6Click 'Execute Node' — confirm the output shows three separate items
What you should see: The Merge node output shows three items: item[0] contains opportunity data, item[1] contains task data, item[2] contains user data.
Common mistake — If your three parallel branches run at slightly different speeds, the Merge node may receive inputs out of order. Lock the order by labeling your HTTP Request nodes clearly and verifying which index each maps to in the Code node before writing transformation logic.
7

n8n Canvas > + Node > Code > Mode: Run Once for All Items

Add a Code node to build the Slack message

This is the core of the workflow. Add a Code node connected to the Merge node. Set it to 'Run Once for All Items' mode. The code extracts opportunity totals by rep, counts overdue tasks per rep, resolves user IDs to names, and assembles a formatted Slack Block Kit message. Paste the code from the Pro Tip section below into the code editor. After pasting, click 'Execute Node' to preview the message output.

  1. 1Add a Code node connected to the Merge node
  2. 2In the right panel, change 'Mode' to 'Run Once for All Items'
  3. 3Delete the default placeholder code
  4. 4Paste the full code from the Pro Tip section into the editor
  5. 5Click 'Execute Node' and inspect the output — it should show a blocks array with formatted Slack sections
What you should see: The Code node output contains a single item with a blocks array. You should see rep names, opportunity counts, pipeline values in dollars, and overdue task counts formatted as Slack Block Kit sections.
Common mistake — If you see 'Cannot read properties of undefined' errors, your Merge node inputs are in the wrong order. Check that $input.all()[0] is opportunities, [1] is tasks, and [2] is users — swap the Merge node connections if needed.

Paste this into the Code node set to 'Run Once for All Items' mode. It reads all three Merge inputs, builds a rep lookup map from Close user IDs, aggregates opportunity values and counts per rep, counts overdue tasks per rep, and returns a Slack Block Kit blocks array ready to pass directly to the Slack node's Blocks field.

JavaScript — Code Node// n8n Code Node — Run Once for All Items
▸ Show code
// n8n Code Node — Run Once for All Items
// Expects Merge node inputs: [0] opportunities, [1] tasks, [2] users
const allItems = $input.all();

... expand to see full code

// n8n Code Node — Run Once for All Items
// Expects Merge node inputs: [0] opportunities, [1] tasks, [2] users

const allItems = $input.all();

// Extract raw data arrays from each Merge input
const opportunities = allItems[0]?.json?.data || [];
const tasks = allItems[1]?.json?.data || [];
const users = allItems[2]?.json?.data || [];

// Build user ID -> display name lookup map
const userMap = {};
for (const user of users) {
  userMap[user.id] = user.display_name || 'Unknown Rep';
}

// Aggregate opportunities per rep
const repPipeline = {};
for (const opp of opportunities) {
  const repId = opp.user_id;
  const repName = userMap[repId] || repId;
  if (!repPipeline[repId]) {
    repPipeline[repId] = { name: repName, oppCount: 0, totalValue: 0, overdueTasks: [], closingThisWeek: 0 };
  }
  repPipeline[repId].oppCount += 1;
  repPipeline[repId].totalValue += opp.value || 0;

  // Flag deals closing within 7 days
  if (opp.date_won) {
    const closeDate = new Date(opp.date_won);
    const today = new Date();
    const diffDays = (closeDate - today) / (1000 * 60 * 60 * 24);
    if (diffDays >= 0 && diffDays <= 7) {
      repPipeline[repId].closingThisWeek += 1;
    }
  }
}

// Aggregate overdue tasks per rep
const today = new Date();
for (const task of tasks) {
  const repId = task.assigned_to;
  const repName = userMap[repId] || repId;
  if (!repPipeline[repId]) {
    repPipeline[repId] = { name: repName, oppCount: 0, totalValue: 0, overdueTasks: [], closingThisWeek: 0 };
  }
  const taskDue = new Date(task.due_date);
  if (taskDue < today) {
    repPipeline[repId].overdueTasks.push(`${task.text || 'Task'} (${task.lead_name || 'Unknown Lead'})`);
  }
}

// Format currency
const fmt = (val) => '$' + val.toLocaleString('en-US', { maximumFractionDigits: 0 });

// Build Slack Block Kit blocks array
const blocks = [];

// Header block
blocks.push({
  type: 'header',
  text: { type: 'plain_text', text: `📊 Pipeline Summary — ${today.toDateString()}`, emoji: true }
});

blocks.push({ type: 'divider' });

// One section per rep (cap at 12 reps to stay under 50-block limit)
const reps = Object.values(repPipeline).slice(0, 12);

for (const rep of reps) {
  const overdueIcon = rep.overdueTasks.length > 0 ? ':red_circle:' : ':white_check_mark:';
  const closingNote = rep.closingThisWeek > 0 ? ` | :fire: ${rep.closingThisWeek} closing this week` : '';

  blocks.push({
    type: 'section',
    text: {
      type: 'mrkdwn',
      text: `*${rep.name}* — ${rep.oppCount} open deals | ${fmt(rep.totalValue)} pipeline${closingNote} | ${overdueIcon} ${rep.overdueTasks.length} overdue task(s)`
    }
  });

  if (rep.overdueTasks.length > 0) {
    blocks.push({
      type: 'context',
      elements: [{
        type: 'mrkdwn',
        text: `Overdue: ${rep.overdueTasks.slice(0, 3).join(' · ')}${rep.overdueTasks.length > 3 ? ` +${rep.overdueTasks.length - 3} more` : ''}`
      }]
    });
  }
}

blocks.push({ type: 'divider' });
blocks.push({
  type: 'context',
  elements: [{ type: 'mrkdwn', text: 'Pulled from Close CRM · Updates daily at 8 AM' }]
});

return [{ json: { blocks } }];
message template
🔔 New Record: {{text}} {{user}}
channel: {{channel}}
ts: {{ts}}
#sales
🔔 New Record: Jane Smith
Company: Acme Corp
8

n8n Sidebar > Settings > Credentials > New Credential > Slack API

Add your Slack credentials to n8n

Go to Settings > Credentials in the n8n sidebar and create a new Slack credential. n8n supports both OAuth2 and Bot Token authentication for Slack. Use a Bot Token — create a Slack app at api.slack.com/apps, add the chat:write scope, install the app to your workspace, and copy the Bot User OAuth Token. Paste it into the Slack credential field in n8n.

  1. 1Go to api.slack.com/apps and click 'Create New App' > 'From scratch'
  2. 2Name it 'Pipeline Bot' and select your workspace
  3. 3Under 'OAuth & Permissions', add the bot scope chat:write
  4. 4Click 'Install to Workspace', authorize, and copy the 'Bot User OAuth Token' (starts with xoxb-)
  5. 5In n8n, go to Settings > Credentials > New Credential > Slack API and paste the token
  6. 6Invite the bot to your target Slack channel with /invite @Pipeline Bot
What you should see: You should see a 'Connection tested successfully' confirmation in n8n and the bot should appear as a member in your Slack channel.
Common mistake — The bot must be explicitly invited to any private channel before it can post. Public channels work automatically after the scope is granted, but private channels return a 'channel_not_found' error until you run /invite.
9

n8n Canvas > + Node > Slack > Message > Send

Add a Slack node to post the pipeline summary

Add a Slack node connected to the Code node. Set the operation to 'Message > Send'. Enter the channel ID (not the channel name — use the ID from the channel's Slack URL or right-click the channel in Slack > Copy Link). Set the 'Blocks' field to use the expression {{ $json.blocks }} from the Code node output. Leave the 'Text' field as a plain fallback like 'Daily pipeline update'.

  1. 1Add a Slack node connected to the Code node
  2. 2Select your Slack credential from the dropdown
  3. 3Set 'Resource' to 'Message' and 'Operation' to 'Send'
  4. 4In the 'Channel' field, paste your channel ID (e.g. C04XXXXXXX)
  5. 5Toggle on 'Blocks' and set the value to the expression {{ JSON.stringify($json.blocks) }}
  6. 6Set 'Text' to 'Daily pipeline update — see blocks for details'
  7. 7Click 'Execute Node' to send a test message
What you should see: You should see the formatted pipeline summary appear in your Slack channel with sections for each rep, their pipeline value, opportunity count, and overdue task count.
Common mistake — Slack's Block Kit has a 50-block limit per message. If your team has more than 12-15 reps, you may hit this limit and get a 'too_many_attachments' error. In the Code node, cap the rep list or split into multiple messages using a Slack node per rep group.
10

n8n Canvas > Right-click node > Add Error Output > Connect to Slack Error Node

Add error handling with a Slack fallback node

Add a second Slack node connected to any step that could fail — the HTTP Request nodes are the most common failure points. Set this node's channel to your internal #ops-alerts channel and configure it to send a plain text error message. In n8n, right-click any node, select 'Add Error Output', and connect that error output to the fallback Slack node.

  1. 1Right-click the first HTTP Request node and select 'Add Error Output'
  2. 2Connect the red error output handle to a new Slack node
  3. 3Configure the Slack node to post to #ops-alerts
  4. 4Set the message text to 'Pipeline workflow failed at Close API pull — check n8n execution log'
  5. 5Repeat for the second and third HTTP Request nodes
What you should see: Three red error output lines appear on your HTTP Request nodes, each connected to the fallback Slack node. If Close's API is down at 8 AM, your #ops-alerts channel receives a notification instead of silently missing the daily update.
11

n8n Canvas > Activate toggle (top right) > Execute Workflow (toolbar)

Activate the workflow and verify the first scheduled run

Click the toggle in the top right of the n8n canvas to activate the workflow. The toggle turns green and the workflow is now live. To verify without waiting until 8 AM, manually trigger it by clicking 'Execute Workflow' in the top toolbar — this fires all nodes immediately regardless of the schedule. Check your Slack channel for the message and the n8n Executions tab for a green success status.

  1. 1Click the gray toggle in the top right — it should turn green and show 'Active'
  2. 2Click 'Execute Workflow' in the toolbar to run it immediately for testing
  3. 3Open your Slack channel and confirm the pipeline summary message arrived
  4. 4Click 'Executions' in the left sidebar to confirm the run shows a green checkmark
  5. 5Check the scheduled trigger settings one more time to confirm the 8 AM daily schedule is correct
What you should see: Your Slack channel shows a formatted pipeline summary message. The Executions tab in n8n shows a green 'Success' status for the manual run. Tomorrow at 8 AM, the workflow fires automatically.
Common mistake — Activating the workflow while the canvas has unsaved changes sometimes locks in the old version. Always click 'Save' before toggling activation — look for the 'Saved' confirmation in the top bar before you activate.
n8n
▶ Run once
executed
Slack
Close
Close
🔔 notification
received

Scaling Beyond More than 100 open opportunities or 200 overdue tasks+ Records

If your volume exceeds More than 100 open opportunities or 200 overdue tasks records, apply these adjustments.

1

Implement pagination for the opportunities endpoint

Close's API returns a maximum of 100 records per request and includes a has_more field in the response. Add a loop in n8n using a Loop Over Items node: check has_more, increment the _skip parameter by 100, and repeat until has_more is false. Merge all pages before passing to the Code node.

2

Filter API results server-side before processing

Instead of pulling all opportunities and filtering in the Code node, add query parameters to narrow the Close API call — for example, filter by date_updated in the last 30 days, or by specific user IDs for the team you're reporting on. Pulling 500 records to display 80 wastes execution time and risks hitting Close's rate limit of 40 requests per second.

3

Split large rep lists into multiple Slack messages

Slack enforces a 50-block limit per message. At 4 blocks per rep (section + context + divider + spacing), you hit the limit at 12 reps. If your team has 20+ reps, split the repPipeline object into chunks of 10 in the Code node and send each chunk as a separate Slack message using multiple Slack nodes chained in sequence.

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 n8n for this if you are self-hosting and want to pay nothing in per-task fees for a workflow that runs 365 times per year. The scheduled trigger, three API calls, a Code node, and a Slack post costs exactly $0 on self-hosted n8n regardless of how many reps are in your team. The second reason to pick n8n is the Code node — building a user ID lookup map, grouping opportunities by rep, and formatting Block Kit JSON is straightforward JavaScript that runs in under 2 seconds. The one scenario where you'd pick something else: if your team is non-technical and the person maintaining this workflow will never touch code, use Zapier's multi-step Zap with Formatter steps instead. It's slower and more expensive, but the UI is more forgiving.

Cost

At one execution per day with five HTTP requests per run, this workflow makes roughly 150 API calls per month to Close and 30 Slack API calls. Close has no published rate limit for read requests at this volume — you won't come close to any ceiling. On n8n Cloud's Starter plan at $20/month, this workflow consumes roughly 31 executions per month (one per day). That's well within the 2,500 included executions. Self-hosted n8n costs nothing in platform fees — just your server, which at $5-10/month on a small VPS handles dozens of workflows. Zapier would run this same workflow for $73/month on the Professional plan because three API calls plus one Slack step = four tasks per Zap run, times 31 runs = 124 tasks/month, which exceeds the free tier's 100 task limit.

Tradeoffs

Make's scenario builder handles this use case well with its HTTP modules and built-in iterator — the scheduled trigger fires cleanly and aggregator modules replace some of what the n8n Code node does manually. Make's free tier includes 1,000 operations/month, and this workflow uses about 6 operations per run, so 186/month — comfortably free. Zapier has no native Close integration as of early 2024, so you'd use Webhooks by Zapier to call the Close API directly, which requires a paid plan. Power Automate has no Close connector and the HTTP action for premium connectors costs extra on top of the per-user license — wrong tool here. Pipedream handles this cleanly with its native Close and Slack actions and includes 10,000 invocations/month free, making it the strongest free-tier competitor to n8n for this use case. n8n wins if you are already self-hosting it; Pipedream wins if you want the fastest setup with zero infrastructure.

Three things you will hit after setup. First, Close's opportunity API uses value in cents for some account configurations and dollars for others depending on account age — if your Slack message shows '$18500000' instead of '$18,500', divide value by 100 in the Code node. Second, the Schedule Trigger in n8n does not back-fill missed executions. If your server is down at 8 AM, the run is skipped entirely — no retry, no catch-up. Add a manual 'Execute Workflow' button shortcut to your browser bookmarks for these situations. Third, Slack's Block Kit context blocks have a 3,000 character limit per element. If a rep has many overdue tasks with long descriptions, the context block text can exceed this and the Slack API returns a 'invalid_blocks' error with no further detail. The Code node caps overdue task display at three items to avoid this — if you remove that cap, you will eventually hit it.

Ideas for what to build next

  • Add a weekly digest with won/lost deal totalsFork the same workflow with a weekly Schedule Trigger and add a second HTTP Request node querying Close's opportunity endpoint filtered by status_type=won and date_won in the past 7 days. Post this summary to a #wins channel every Friday at 4 PM.
  • Send individual rep summaries as Slack DMsInstead of posting to a shared channel, use Close's user email addresses to look up each rep's Slack user ID via the Slack users.lookupByEmail API endpoint, then send each rep a personalized DM with only their own deals and tasks.
  • Trigger a Close task when a rep doesn't respond to overdue itemsAdd a follow-up branch: if a rep has 3+ overdue tasks for two consecutive days, use the Close task API to automatically create a follow-up task assigned to the sales manager. This creates an escalation loop without requiring manual oversight.

Related guides

Was this guide helpful?
Slack + Close overviewn8n profile →