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

How to Send Close Lead Assignments to Slack with n8n

When a lead is assigned in Close, n8n fires a webhook, pulls company size, lead source, and contact details, then posts a formatted message to the assigned rep's dedicated Slack channel.

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

Best for

Inside sales teams with dedicated per-rep Slack channels who need instant context on new leads without opening Close

Not ideal for

Teams with more than 20 reps or dynamic channel naming — use Zapier's built-in Slack lookup table instead to avoid maintaining a static channel map in n8n

Sync type

real-time

Use case type

notification

Real-World Example

💡

A 12-person SaaS sales team uses this to ping each rep's private Slack channel the moment Close assigns them a lead. Before this workflow, reps got assignment emails buried in their inbox and would sometimes wait 45 minutes before making first contact. Now the Slack message hits within 10 seconds of assignment and includes company size, lead source, and the primary phone number so the rep can dial immediately.

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 Leads and Contacts — found under Settings > Your API Keys
Slack bot token with chat:write and channels:read scopes — create via api.slack.com/apps
n8n instance running on at least v1.0 (cloud or self-hosted) with the Webhook node accessible from the public internet
Each rep's Slack channel name mapped to their Close user ID — you'll need this for the lookup table in Step 6
Your Slack bot invited to every rep's target channel before activation — private channels block uninvited bots silently

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Lead IDid
Assigned User IDassigned_to
Lead Display Namedisplay_name
Primary Contact Namecontacts[0].name
Changed Fields Arraychanged_fields
5 optional fields▸ show
Lead Sourcesource
Lead Statusstatus_label
Primary Contact Emailcontacts[0].emails[0].email
Primary Phone Numbercontacts[0].phones[0].phone
Company Sizecustom.cf_YOUR_FIELD_KEY

Step-by-Step Setup

1

Close > Settings > Integrations > Webhooks > Add Webhook

Create a webhook in Close

Log into Close and go to Settings > Integrations > Webhooks. Click 'Add Webhook'. You'll enter a URL here — leave the tab open because you'll paste n8n's URL in the next step. Set the event to 'Lead Updated' since Close fires assignment changes under that event type, not a dedicated assignment event.

  1. 1Click your avatar in the bottom-left corner of Close
  2. 2Select 'Settings' from the dropdown
  3. 3Click 'Integrations' in the left sidebar
  4. 4Click 'Webhooks', then click the blue 'Add Webhook' button
  5. 5Select 'Lead Updated' as the event type and leave the URL field blank for now
What you should see: You should see a partially configured webhook row with 'Lead Updated' selected and a blank URL field ready for the next step.
Common mistake — Close does not have a dedicated 'Lead Assigned' event. 'Lead Updated' fires for every field change on a lead — company name edits, tag changes, everything. Your n8n workflow must filter for assignment changes specifically, or you'll flood Slack with noise.
2

n8n > New Workflow > + Add Node > Webhook

Set up the n8n Webhook trigger node

In n8n, create a new workflow and add a Webhook node as the trigger. Set the HTTP method to POST. n8n will generate a unique webhook URL — copy it. Switch back to Close and paste this URL into the webhook configuration you started in Step 1. Save the Close webhook.

  1. 1Click '+ New Workflow' in the n8n sidebar
  2. 2Click the '+' node button and search for 'Webhook'
  3. 3Set HTTP Method to 'POST'
  4. 4Copy the generated Production URL shown in the node panel
  5. 5Paste the URL into the Close webhook URL field and save
What you should see: The n8n Webhook node shows a green Production URL. The Close webhook row shows the URL you pasted and displays a 'Saved' confirmation.
Common mistake — n8n gives you both a Test URL and a Production URL. During setup use the Test URL so you can inspect live payloads. Switch to the Production URL before going live — Close will not retry failed deliveries, so if you forget to switch, assignments will fire to a dead endpoint.
n8n
+
click +
search apps
Slack
SL
Slack
Set up the n8n Webhook trigg…
Slack
SL
module added
3

n8n > Webhook Node > Listen for Test Event

Test the webhook with a real Close assignment

In n8n, click 'Test Workflow' so the Webhook node starts listening. Go to Close, open any lead, and change the assigned user to someone else. Return to n8n — the Webhook node should show a green execution with a full payload. Inspect the JSON to confirm you can see the 'assigned_to' field and the lead's contact data.

  1. 1Click 'Test Workflow' in the top toolbar of your n8n canvas
  2. 2Open Close in a separate tab and open any lead record
  3. 3Change the 'Assigned To' field to a different user and save
  4. 4Return to n8n and click the Webhook node to view the incoming payload
What you should see: The Webhook node shows a blue dot and displays the raw JSON payload from Close, including fields like 'assigned_to', 'display_name', 'contacts', and 'custom'.
Common mistake — Copy the webhook URL carefully — it expires if you regenerate it, and any scenarios using the old URL will silently stop working.
n8n
▶ Run once
executed
Slack
Close
Close
🔔 notification
received
4

n8n Canvas > + Add Node > IF

Add an IF node to filter assignment changes only

Add an IF node directly after the Webhook node. Close's 'Lead Updated' payload includes a 'changed_fields' array listing which fields changed. Set your IF condition to check whether 'assigned_to' appears in that array. Only route payloads that contain this value down the 'true' branch — everything else hits the 'false' branch and stops.

  1. 1Click the '+' connector on the right side of the Webhook node
  2. 2Search for 'IF' and add it
  3. 3Set Condition to: String, Value 1 = {{ $json.changed_fields.join(',') }}, Operation = 'Contains', Value 2 = 'assigned_to'
  4. 4Leave the 'false' branch unconnected — it will simply stop execution
What you should see: The IF node shows two output branches labeled 'true' and 'false'. A test run with the assignment payload should route to the 'true' branch.
Common mistake — The 'changed_fields' field in the Close payload is an array, not a string. Using a direct string comparison will fail. Join the array into a comma-separated string first using .join(',') as shown, or the filter will never match.
Slack
SL
trigger
filter
Condition
matches criteria?
yes — passes through
no — skipped
Close
CL
notified
5

n8n Canvas > + Add Node > HTTP Request

Fetch full lead details from Close API

The webhook payload contains a lead ID but may not include all the fields you want in your Slack message — specifically custom fields like company size. Add an HTTP Request node on the 'true' branch to GET the full lead record from Close's REST API. Use the lead ID from the webhook payload in the URL.

  1. 1Click the '+' connector on the 'true' output of the IF node
  2. 2Search for 'HTTP Request' and add it
  3. 3Set Method to 'GET'
  4. 4Set URL to: https://api.close.com/api/v1/lead/{{ $('Webhook').item.json.data.id }}/
  5. 5Under Authentication, select 'Basic Auth' and enter your Close API key as the username with a blank password
What you should see: The HTTP Request node returns a 200 response with the full lead JSON, including contacts array, custom fields, and the assigned_to user ID.
Common mistake — Close's API uses Basic Auth with your API key as the username and an empty string as the password. If you set the password to anything, the request returns 401. Leave the password field completely blank.
6

n8n Canvas > + Add Node > Code

Resolve the assigned user's name and Slack channel

The Close payload gives you an assigned_to value like 'user_abc123' — not a human name and not a Slack channel. Add a Code node to map this user ID to a display name and their Slack channel. Define a static lookup object inside the code. This is the most maintainable approach for teams under 20 reps.

  1. 1Click the '+' connector on the HTTP Request node output
  2. 2Search for 'Code' and add it
  3. 3Select 'Run Once for All Items' mode
  4. 4Paste the mapping code (see pro tip) into the JavaScript editor
  5. 5Click 'Test Step' to confirm the output includes repName and slackChannel fields
What you should see: The Code node output shows items with two new fields: 'repName' (e.g. 'Sarah Chen') and 'slackChannel' (e.g. '#leads-sarah') alongside all existing lead data.
Common mistake — If a new rep is added to Close and isn't in your lookup table, the Code node will return undefined for their channel. Add a fallback channel (e.g. '#leads-unassigned') in the code, or unassigned leads will post to a null channel and the Slack node will throw a silent error.

This single Code node handles both the user-to-channel lookup and the Slack Block Kit message assembly. Paste it in Step 6's Code node, replacing the two separate Code nodes if you prefer a single step. Update the 'repLookup' object with your actual Close user IDs and Slack channel names before testing.

JavaScript — Code Node// n8n Code Node — Lead Assignment Notification Builder
▸ Show code
// n8n Code Node — Lead Assignment Notification Builder
// Combines user lookup + Block Kit message formatting
// Paste into a Code node connected after the HTTP Request (Close API) node

... expand to see full code

// n8n Code Node — Lead Assignment Notification Builder
// Combines user lookup + Block Kit message formatting
// Paste into a Code node connected after the HTTP Request (Close API) node

const lead = $('HTTP Request').item.json;
const webhookData = $('Webhook').item.json;

// Map Close user IDs to rep names and Slack channels
// Replace these with your actual Close user IDs
const repLookup = {
  'user_7f3kd92': { name: 'James Okafor', channel: '#leads-james' },
  'user_4a1mn58': { name: 'Sarah Chen', channel: '#leads-sarah' },
  'user_9x2pq71': { name: 'Marcus Webb', channel: '#leads-marcus' },
};

const assignedUserId = webhookData.data.assigned_to;
const rep = repLookup[assignedUserId] || { name: 'Unknown Rep', channel: '#leads-unassigned' };

// Pull primary contact details safely
const contacts = lead.contacts || [];
const primaryContact = contacts[0] || {};
const contactName = primaryContact.name || 'No contact listed';
const contactEmail = (primaryContact.emails && primaryContact.emails[0]) ? primaryContact.emails[0].email : 'No email';
const contactPhone = (primaryContact.phones && primaryContact.phones[0]) ? primaryContact.phones[0].phone : 'No phone';

// Pull custom fields — update key to match your Close account
const companySize = lead.custom && lead.custom['cf_company_size'] ? lead.custom['cf_company_size'] : 'Unknown';

const leadUrl = `https://app.close.com/leads/${lead.id}/`;

// Build Slack Block Kit payload
const blocks = [
  {
    type: 'header',
    text: { type: 'plain_text', text: `🎯 New Lead Assigned: ${lead.display_name}`, emoji: true }
  },
  {
    type: 'section',
    fields: [
      { type: 'mrkdwn', text: `*Assigned To:*\n${rep.name}` },
      { type: 'mrkdwn', text: `*Lead Source:*\n${lead.source || 'Not set'}` },
      { type: 'mrkdwn', text: `*Company Size:*\n${companySize}` },
      { type: 'mrkdwn', text: `*Status:*\n${lead.status_label || 'Unknown'}` }
    ]
  },
  {
    type: 'section',
    fields: [
      { type: 'mrkdwn', text: `*Contact:*\n${contactName}` },
      { type: 'mrkdwn', text: `*Phone:*\n${contactPhone}` },
      { type: 'mrkdwn', text: `*Email:*\n${contactEmail}` }
    ]
  },
  {
    type: 'actions',
    elements: [
      {
        type: 'button',
        text: { type: 'plain_text', text: 'Open in Close', emoji: true },
        url: leadUrl,
        style: 'primary'
      }
    ]
  }
];

return [{
  json: {
    slackChannel: rep.channel,
    repName: rep.name,
    blocks: JSON.stringify(blocks),
    fallbackText: `New lead assigned to ${rep.name}: ${lead.display_name} (${contactPhone})`,
    leadId: lead.id,
    leadUrl: leadUrl
  }
}];
7

n8n Canvas > + Add Node > Code

Format the Slack message with lead details

Add another Code node to build the final Slack Block Kit message. Block Kit gives you structured sections with bold labels, making the lead details scannable at a glance. Pull company name, lead source, company size, primary contact name, and primary phone from the HTTP Request node's output.

  1. 1Click the '+' connector on the user-lookup Code node
  2. 2Add another Code node
  3. 3Build the blocks array with a header block and a fields section block
  4. 4Reference lead data using $('HTTP Request').item.json syntax
  5. 5Include a fallback text property for Slack clients that don't render Block Kit
What you should see: The Code node outputs a JSON object with a 'blocks' array and a 'text' fallback string, ready to be passed directly to the Slack node.
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.
8

n8n Canvas > + Add Node > Slack > Send Message

Add and configure the Slack node

Add the Slack node and connect your Slack account via OAuth. Set the Resource to 'Message' and Operation to 'Send'. Set the Channel field to the dynamic slackChannel value from your Code node output. Set the Message field to the blocks array. Make sure you toggle 'Send as Bot' on so the message comes from your integration bot, not your personal Slack account.

  1. 1Click '+' and search for 'Slack', then select the Slack node
  2. 2Click 'Create New Credential' and complete the OAuth flow with your Slack workspace
  3. 3Set Resource to 'Message' and Operation to 'Send'
  4. 4Set Channel to: {{ $json.slackChannel }}
  5. 5Enable 'Blocks' toggle and paste in the blocks reference, set Text fallback
What you should see: The Slack node shows a green credential badge. Running the test delivers a formatted message to the target Slack channel with lead details visible.
Common mistake — Your Slack bot must be manually invited to each rep's channel before it can post. If the channel is private and the bot hasn't been invited, the Slack API returns 'channel_not_found' even though the channel exists. Run /invite @YourBotName in each channel before activating the workflow.
9

n8n > Workflow Settings > Error Workflow

Add error handling with an Error Trigger node

Close does not retry failed webhook deliveries. If your Slack node fails — rate limit, wrong channel, token expiry — the assignment notification is lost silently. Add a separate Error Workflow in n8n's workflow settings that pings a #alerts channel with the failed execution ID. This takes 3 minutes and saves real lead loss.

  1. 1Click the three-dot menu at the top of the canvas and select 'Settings'
  2. 2Under 'Error Workflow', click 'Select Workflow' and create a new one
  3. 3In the new error workflow, add an Error Trigger node and connect it to a Slack node
  4. 4Configure the Slack node to post to #alerts with the message: 'Lead notification failed. Execution: {{ $json.executionId }}'
  5. 5Save both workflows
What you should see: The main workflow's Settings panel shows your error workflow linked. A test failure (e.g. wrong Slack channel name) should produce an alert in your #alerts channel within 30 seconds.
10

n8n > Workflow Canvas > Activate toggle (top right)

Activate the workflow and validate end-to-end

Switch the Webhook node's URL from Test to Production in the Close webhook settings. Toggle the workflow to Active in n8n. Assign a real lead in Close to a real rep and confirm the message appears in their Slack channel within 15 seconds. Check that the company size, lead source, and contact fields are all populated correctly.

  1. 1Go back to Close Settings > Integrations > Webhooks and update the URL to the n8n Production URL
  2. 2In n8n, click the grey 'Inactive' toggle in the top-right of the canvas to set it to 'Active'
  3. 3Open Close and reassign a test lead to a rep whose channel is set up
  4. 4Check the rep's Slack channel for the notification within 15 seconds
  5. 5Open n8n's Executions log and confirm the run shows green
What you should see: The Executions panel shows a successful green run. The rep's Slack channel contains a formatted message with company name, size, lead source, and contact details. No errors in the error workflow.

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 your team self-hosts infrastructure, wants zero per-task cost at scale, or needs the Code node to handle non-standard Close custom fields. n8n's webhook-to-Slack path adds roughly 3-5 seconds of latency on self-hosted instances with modest hardware, which is fine for sales notifications. The one case where you should pick Zapier instead: your ops person doesn't write code and you have more than 15 reps with complex routing logic. Zapier's Slack lookup table and Paths feature handle dynamic channel routing without touching a single line of JavaScript.

Cost

Cost math is simple. This workflow runs once per lead assignment — one webhook execution, one HTTP Request to Close, one Slack post. At 200 lead assignments per month on n8n Cloud, you're using 600 operations (three nodes firing per execution). n8n Cloud's Starter plan includes 2,500 operations for $20/month, so you have headroom for 833 assignments before hitting the ceiling. Self-hosted n8n costs you $0 in platform fees — just server costs, typically $5-10/month on a small VPS. Zapier would run the same 200 assignments on their Professional plan at $49/month. n8n saves you $29-44/month for the identical outcome.

Tradeoffs

Make handles this use case well with its built-in Close module and Slack Send Message action — no HTTP Request node needed, which cuts one step. Make's visual data mapping is faster to configure than n8n's Code node for anyone unfamiliar with JavaScript. Zapier has a Close trigger called 'New Lead' but lacks a native 'Lead Assigned' trigger, forcing you to use webhooks anyway — same complexity as n8n, higher cost. Power Automate has no Close connector at all, so you'd be writing custom HTTP actions for everything; it's the wrong tool here. Pipedream's Close component is maintained and handles the webhook trigger cleanly, but the free tier caps at 10,000 invocations per day — more than enough, though Pipedream's pricing jumps steeply if you add compute-heavy steps. n8n wins on cost and code flexibility for teams already running it.

Three things you'll hit after setup. First: Close's 'Lead Updated' event fires for status changes, tag edits, note additions — anything. If you misconfigure the IF filter, you'll blast Slack with every single edit. Test the filter explicitly with a non-assignment update before going live. Second: Close custom fields use auto-generated keys like 'cf_7a3bc91' that are unique to your organization. The field you call 'Company Size' in the UI maps to a different key in every Close account — always check the raw API response to confirm the exact key before deploying. Third: Slack's Block Kit 'button' element with a URL requires your Slack app to have the links:write scope enabled, or the button renders without the clickable link. Check your app scopes at api.slack.com/apps if the 'Open in Close' button appears but doesn't work.

Ideas for what to build next

  • Add lead score or deal value to the notificationPull additional Close custom fields like estimated deal value or lead score into the Block Kit message. Reps can then prioritize callbacks within their queue without opening Close.
  • Build a daily digest of uncontacted assigned leadsAdd a second workflow on a Schedule trigger that queries Close's API for leads assigned in the last 24 hours with no logged calls. Post a digest to each rep's channel every morning at 8am.
  • Log rep acknowledgement back to CloseAdd a Slack interactive button to the notification that lets reps click 'Acknowledged' — use n8n's Webhook node to catch that click and log a note on the Close lead automatically.

Related guides

Was this guide helpful?
Slack + Close overviewn8n profile →