The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "WF-NW-02 \u2014 Daily 500 \u2192 Close + First SMS",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "30 7,8,9,10 * * *"
}
]
}
},
"id": "trg-daily-9am",
"name": "Daily 7:30/8:30/9:30/10:30 AM ET",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
-16,
208
]
},
{
"parameters": {
"url": "https://YOUR_CONVEX_DEPLOYMENT/daily-batch?limit=100",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"options": {
"timeout": 60000
}
},
"id": "http-get-batch",
"name": "GET Convex /daily-batch",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-32,
0
],
"retryOnFail": true,
"maxTries": 2,
"waitBetweenTries": 3000
},
{
"parameters": {
"jsCode": "// TEST MODE: limit to 100 leads. Remove TEST_LIMIT to restore full production.\nconst TEST_LIMIT = 100;\nconst out = [];\nfor (const item of $input.all()) {\n const arr = Array.isArray(item.json) ? item.json : [item.json];\n for (const lead of arr) {\n out.push({ json: lead });\n if (out.length >= TEST_LIMIT) return out;\n }\n}\nreturn out;\n"
},
"id": "code-split",
"name": "Split to items",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
336,
0
]
},
{
"parameters": {
"options": {}
},
"id": "split-batches",
"name": "Loop Batches (10)",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
672,
0
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "cond-haswebsite",
"leftValue": "={{ $json.has_website }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-haswebsite",
"name": "Has Website?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
880,
16
]
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT/flag-website",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ id: $json.id, website: $json.website }) }}",
"options": {}
},
"id": "http-flag-website",
"name": "Convex /flag-website (skip)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1184,
-208
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://api.close.com/api/v1/lead/",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ (function(){ var l = $('Has Website?').item.json; var VALID_LT = {mobile:1, voip:1, fixed_line:1, fixed_line_or_mobile:1}; var line = (l.phone_type && VALID_LT[l.phone_type]) ? l.phone_type : undefined; var TZ = {OH:'ET',MI:'ET',NC:'ET',SC:'ET',GA:'ET',FL:'ET',VA:'ET',WV:'ET',PA:'ET',NY:'ET',NJ:'ET',CT:'ET',MA:'ET',RI:'ET',VT:'ET',NH:'ET',ME:'ET',MD:'ET',DE:'ET',DC:'ET',KY:'ET',IN:'ET',AL:'CT',LA:'CT',MS:'CT',TN:'CT',AR:'CT',MO:'CT',IA:'CT',MN:'CT',WI:'CT',IL:'CT',OK:'CT',TX:'CT',KS:'CT',NE:'CT',SD:'CT',ND:'CT',MT:'MT',WY:'MT',CO:'MT',NM:'MT',UT:'MT',AZ:'MT',ID:'MT',CA:'PT',OR:'PT',WA:'PT',NV:'PT'}; var OVR = {'KY_paducah':'CT','KY_owensboro':'CT','KY_hopkinsville':'CT','KY_bowling green':'CT','KY_henderson':'CT','KY_madisonville':'CT','KY_fulton':'CT','KY_mayfield':'CT','KY_murray':'CT','KY_princeton':'CT','TN_knoxville':'ET','TN_chattanooga':'ET','TN_kingsport':'ET','TN_johnson city':'ET','TN_bristol':'ET','TN_oak ridge':'ET','TN_cleveland':'ET','TN_maryville':'ET','TN_sevierville':'ET','TN_greeneville':'ET','TN_morristown':'ET','TN_athens':'ET','FL_pensacola':'CT','FL_panama city':'CT','FL_fort walton beach':'CT','IN_evansville':'CT','IN_jasper':'CT','IN_vincennes':'CT','MI_iron mountain':'CT','MI_ironwood':'CT','TX_el paso':'MT','TX_van horn':'MT','OR_ontario':'MT','KS_goodland':'MT','NE_scottsbluff':'MT','NE_gering':'MT','NE_chadron':'MT','ND_dickinson':'MT','SD_rapid city':'MT','SD_spearfish':'MT'}; var st = String(l.state||'').toUpperCase().trim(); var ct = String(l.city||'').toLowerCase().trim(); var tz = OVR[st+'_'+ct] || TZ[st] || undefined; var body = { name: l.business_name, url: l.website || undefined, contacts: [{ name: l.owner_name || l.business_name, emails: l.owner_email ? [{ email: l.owner_email, type: 'office' }] : [], phones: [{ phone: l.phone, type: 'mobile' }] }], 'custom.cf_u4Hy4gZaeEGKM6hmtoXCvKHqxT5kLAMga9XE6HKa5cG': l.niche, 'custom.cf_FoME7N83pc39pZ45Oa9AgS0n9om7ZnHkPRMiaLE6WXw': l.address, 'custom.cf_qi2n7BVM6AwYTv9AR4wyNBZDOP2SneLucX5NKNN150o': l.city, 'custom.cf_saFSzrigpdmJraiY8Tj6PC2HYnieWXrxvlZ8ycUd3gp': l.state, 'custom.cf_vqMAlwS6Wftd2Bl0jykhJvDuogQjghng9eeRuceDnpp': l.zip, 'custom.cf_amnmRrlaPBheatw5a11gKcTHsOEYsXn9y4Q4eRAEfrf': l.google_rating, 'custom.cf_nyGJLfXTqtB4kTY4Vg1BEe67HRjvpqLXB0DIxGDOlKZ': l.google_reviews_count, 'custom.cf_wOj9YA0SuwimkXa5dDv3UoJJWWeMHBxX5Pqw9qrK9Gb': l.google_maps_url, 'custom.cf_j6QkeiU0nlzk0kgngkIENCSHEu7LxJ3NsBN0TgvkzKT': l.sender_phone || '+15555550100', 'custom.cf_MuBbV1mNj2o47xuMYhTyVYaqwaqaban5s8wxkeYdvZf': 'False', 'custom.cf_PcdRHPUTgoPxgqczBOFs5eDrhVFAqYcez63lQgCJyJK': 0, 'custom.cf_QaXxtdAmTEByJ8SOBFk4wizgJwrwZF7XtSufDkTCP2x': l.id, 'custom.cf_bWgM0fxF58n1BO5YElBmJcuTV1CzDw5sIGY3wa3b51u': 'reviewsystem' }; if (line) body['custom.cf_CpsUzGm98lRUY7ah9LUhfFGGC6xwYE7ZYsBj1DcPgTf'] = line; if (tz) body['custom.cf_BkcfnGTkMu0BnsxogmbuVQ746I4m4vqaTdZkQG5LYsV'] = tz; return JSON.stringify(body); })() }}",
"options": {}
},
"id": "http-close-create",
"name": "Close POST /lead/",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1088,
32
],
"retryOnFail": false,
"maxTries": 1,
"waitBetweenTries": 2000,
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT/mark-pushed",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ id: $('Has Website?').item.json.id, close_lead_id: $json.id, close_contact_id: ($json.contacts && $json.contacts[0]) ? $json.contacts[0].id : undefined, sender_phone: $('Has Website?').item.json.sender_phone, sender_label: $('Has Website?').item.json.sender_label }) }}",
"options": {}
},
"id": "http-mark-pushed",
"name": "Convex /mark-pushed",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1280,
32
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://api.close.com/api/v1/activity/sms/",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ (function(){ var l = $('Has Website?').item.json; var lc = $('Close POST /lead/').item.json; var PLURAL = {'plumbing':'plumbing companies','plumber':'plumbing companies','roofing':'roofing companies','roofer':'roofing companies','hvac':'HVAC companies','HVAC':'HVAC companies','electrical':'electricians','electrician':'electricians','concrete':'concrete companies','fencing':'fencing companies','fence':'fencing companies','tree service':'tree services','tree services':'tree services','septic':'septic companies','septic service':'septic companies','landscaping':'landscaping companies','paving':'paving companies','pool':'pool companies','pool installation':'pool companies','garage door':'garage door companies','foundation':'foundation companies','foundation repair':'foundation companies','painting':'painting companies','pressure washing':'pressure washing companies','remodeling':'remodelers','deck builder':'deck builders','junk removal':'junk removal companies','general contractor':'general contractors','gc':'general contractors','dentist':'dentists','chiropractor':'chiropractors','med spa':'med spas','auto repair':'auto shops','pest control':'pest control companies'}; var n = String(l.niche||'').toLowerCase().trim(); var niche = PLURAL[n] || (n + ' companies'); var city = l.city || l.state || 'the area'; var name = l.business_name || 'your shop'; var dayRef; var d = new Date().getDay(); if (d === 0 || d === 6 || d === 1 || d === 2) { dayRef = 'over the weekend'; } else { var names = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; dayRef = 'on ' + names[d - 2]; } var text = 'I was looking up ' + niche + ' in ' + city + ' ' + dayRef + ' and ' + name + ' came up... checked the reviews first like everyone does. Noticed the businesses above you on Google are doing one specific thing that\\'s winning them the calls. Want me to send what I found?'; return JSON.stringify({ lead_id: lc.id, contact_id: (lc.contacts && lc.contacts[0]) ? lc.contacts[0].id : undefined, direction: 'outbound', status: 'outbox', local_phone: l.sender_phone || '+15555550100', remote_phone: l.phone, text: text }); })() }}",
"options": {}
},
"id": "http-close-sms-opener",
"name": "Close SMS \u2014 Opener (Text 1)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1504,
-224
],
"retryOnFail": false,
"maxTries": 1,
"waitBetweenTries": 2000,
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT/record-sms",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ id: $('Has Website?').item.json.id }) }}",
"options": {}
},
"id": "http-record-sms",
"name": "Convex /record-sms",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1568,
32
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "PUT",
"url": "=https://api.close.com/api/v1/lead/{{ $('Close POST /lead/').item.json.id }}/",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ 'custom.cf_PcdRHPUTgoPxgqczBOFs5eDrhVFAqYcez63lQgCJyJK': 1, status_id: 'stat_K1gvuxcKTCcLJ0Ud5COx1PyM8GOFWIPVNfSnUlGD0pM' }) }}",
"options": {}
},
"id": "http-close-cf-smscount",
"name": "Close PUT cf SMS Count",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1936,
32
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"amount": 3,
"unit": "seconds"
},
"id": "wait-throttle",
"name": "Wait 1.5s (rate-limit)",
"type": "n8n-nodes-base.wait",
"typeVersion": 1,
"position": [
1840,
336
]
},
{
"parameters": {
"jsCode": "// Null-safe summary \u2014 references nodes only if they ran\nlet total = 0, created = 0, skipped = 0;\ntry { total = $('Has Website?').all().length; } catch(e) {}\ntry { created = $('Close POST /lead/').all().filter(function(x){ return x.json && x.json.id; }).length; } catch(e) {}\ntry { skipped = $('Convex /flag-website (skip)').all().length; } catch(e) {}\nconst webhook = 'YOUR_SLACK_WEBHOOK';\nawait this.helpers.httpRequest({ method: 'POST', url: webhook, body: { text: '\ud83d\udce4 *WF-NW-02 Daily Batch* \u2014 pulled ' + total + ', created ' + created + ' Close leads + sent opener SMS, skipped ' + skipped + ' (had website)' }, json: true });\nreturn [{ json: { total: total, created: created, skipped: skipped } }];"
},
"id": "code-slack-summary",
"name": "Slack: send summary",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
800,
-144
]
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT/sms-activities/log",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ lead_id: $('Has Website?').item.json.id, close_lead_id: $('Close POST /lead/').item.json.id, stage: 0, direction: 'outbound', text: $('Close SMS \u2014 Opener (Text 1)').item.json.text || '', sent_at: Date.now(), close_activity_id: $json.id, status: $json.status || 'outbox', sender_label: $('Has Website?').item.json.sender_label, sender_phone: $('Has Website?').item.json.sender_phone }) }}",
"options": {}
},
"id": "http-log-sms-activity",
"name": "Convex /sms-activities/log (opener)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1760,
32
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "// Assign one of 11 AI-labeled Close phone numbers to each lead.\n// Cap = 100 SMS per sender per ET day.\n// Reads today's per-sender count from Convex sms_activities (1 HTTP call, no external API).\nvar SENDERS = [\n { label: 'AI', phone: '+15555550100' },\n { label: 'AI 2', phone: '+15555550100' },\n { label: 'AI 3', phone: '+15555550100' },\n { label: 'AI 4', phone: '+15555550100' },\n { label: 'AI 5', phone: '+15555550100' },\n { label: 'AI 6', phone: '+15555550100' },\n { label: 'AI 7', phone: '+15555550100' },\n { label: 'AI 8', phone: '+15555550100' },\n { label: 'AI 9', phone: '+15555550100' },\n { label: 'AI 10', phone: '+15555550100' },\n { label: 'AI 11', phone: '+15555550100' }\n];\nvar MAX_PER_SENDER = 100;\n\n// Ask Convex how many outbound SMS each sender has already sent today\nvar res = await this.helpers.httpRequest({\n method: 'GET',\n url: 'https://YOUR_CONVEX_DEPLOYMENT/senders/today',\n headers: { Authorization: 'YOUR_BEARER_TOKEN' },\n json: true\n});\nvar perPhone = (res && res.perPhone) || {};\n\n// Walk senders in order; fill each up to 100\nvar items = $input.all();\nvar senderIdx = 0;\nvar usedInSlot = perPhone[SENDERS[0].phone] || 0;\n\nfor (var i = 0; i < items.length; i++) {\n while (senderIdx < SENDERS.length && usedInSlot >= MAX_PER_SENDER) {\n senderIdx++;\n usedInSlot = senderIdx < SENDERS.length ? (perPhone[SENDERS[senderIdx].phone] || 0) : MAX_PER_SENDER;\n }\n if (senderIdx >= SENDERS.length) {\n items[i].json.sender_label = null;\n items[i].json.sender_phone = null;\n items[i].json.sender_full = true;\n } else {\n items[i].json.sender_label = SENDERS[senderIdx].label;\n items[i].json.sender_phone = SENDERS[senderIdx].phone;\n items[i].json.sender_full = false;\n usedInSlot++;\n }\n}\nreturn items;\n"
},
"id": "code-assign-senders",
"name": "Assign Senders (AI1..AI11, 100/day)",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-48,
-224
]
},
{
"parameters": {
"url": "=https://api.close.com/api/v1/lead/?query=phone:{{ $json.phone }}&_fields=id&_limit=1",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"options": {
"timeout": 15000
}
},
"id": "http-dup-check",
"name": "Close Duplicate Check",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
416,
336
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "c-dup",
"leftValue": "={{ Number($json.total_results || 0) }}",
"rightValue": 0,
"operator": {
"type": "number",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-not-dup",
"name": "Not Duplicate?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
608,
336
]
},
{
"parameters": {
"method": "POST",
"url": "YOUR_SLACK_WEBHOOK",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ text: '\ud83d\udd01 *DUPLICATE SKIPPED* \u00b7 ' + ($('Has Website?').item.json.business_name || '?') + ' \u00b7 phone ' + ($('Has Website?').item.json.phone || '?') + ' already in Close CRM' }) }}",
"options": {}
},
"id": "slack-dup",
"name": "Slack: Duplicate Skipped",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
704,
560
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "var NPA_TZ = {\"405\":\"America/Chicago\",\"410\":\"America/New_York\",\"901\":\"America/Chicago\",\"520\":\"America/Phoenix\",\"502\":\"America/New_York\",\"915\":\"America/Chicago\",\"989\":\"America/New_York\",\"313\":\"America/New_York\",\"480\":\"America/Phoenix\",\"720\":\"America/Denver\",\"702\":\"America/Los_Angeles\",\"303\":\"America/Denver\",\"404\":\"America/New_York\",\"678\":\"America/New_York\",\"414\":\"America/Chicago\",\"612\":\"America/Chicago\",\"443\":\"America/New_York\",\"763\":\"America/Chicago\",\"812\":\"America/Chicago\",\"256\":\"America/Chicago\",\"617\":\"America/New_York\",\"623\":\"America/Phoenix\",\"539\":\"America/Chicago\",\"505\":\"America/Los_Angeles\",\"330\":\"America/New_York\",\"903\":\"America/New_York\",\"918\":\"America/Chicago\",\"205\":\"America/Chicago\",\"615\":\"America/Chicago\",\"704\":\"America/Denver\",\"983\":\"America/New_York\",\"770\":\"America/New_York\",\"618\":\"America/Chicago\",\"857\":\"America/New_York\",\"806\":\"America/Chicago\",\"508\":\"America/New_York\",\"239\":\"America/New_York\",\"952\":\"America/Chicago\",\"229\":\"America/Chicago\",\"301\":\"America/New_York\",\"402\":\"America/New_York\",\"725\":\"America/Los_Angeles\",\"765\":\"America/Chicago\",\"223\":\"America/Chicago\",\"662\":\"America/New_York\",\"706\":\"America/New_York\",\"240\":\"America/New_York\",\"347\":\"America/New_York\",\"212\":\"America/New_York\",\"646\":\"America/New_York\",\"516\":\"America/New_York\",\"332\":\"America/New_York\",\"929\":\"America/New_York\",\"917\":\"America/New_York\",\"718\":\"America/New_York\",\"252\":\"America/New_York\",\"507\":\"America/Chicago\",\"512\":\"America/Chicago\",\"501\":\"America/Chicago\",\"737\":\"America/Chicago\",\"517\":\"America/New_York\",\"614\":\"America/New_York\",\"406\":\"America/New_York\",\"979\":\"America/New_York\",\"828\":\"America/New_York\",\"920\":\"America/New_York\",\"609\":\"America/New_York\",\"380\":\"America/New_York\",\"323\":\"America/Los_Angeles\",\"972\":\"America/Chicago\",\"214\":\"America/Chicago\",\"570\":\"America/New_York\",\"469\":\"America/Chicago\",\"503\":\"America/Los_Angeles\",\"817\":\"America/Chicago\",\"945\":\"America/Chicago\",\"682\":\"America/Chicago\",\"317\":\"America/New_York\",\"424\":\"America/Los_Angeles\",\"215\":\"America/New_York\",\"315\":\"America/New_York\",\"267\":\"America/New_York\",\"610\":\"America/New_York\",\"272\":\"America/New_York\",\"602\":\"America/Phoenix\",\"760\":\"America/Los_Angeles\",\"970\":\"America/Denver\",\"210\":\"America/Chicago\",\"726\":\"America/Chicago\",\"346\":\"America/Chicago\",\"858\":\"America/Los_Angeles\",\"281\":\"America/Chicago\",\"832\":\"America/Chicago\",\"713\":\"America/Chicago\",\"254\":\"America/Chicago\"};\n// SEND WINDOW: 7:30 AM \u2014 8:00 PM lead's LOCAL time\nvar START_MIN = 7 * 60 + 30; // 7:30 AM = 450\nvar END_MIN = 20 * 60; // 8:00 PM = 1200\nfunction npaTz(p){ var d=String(p||'').replace(/\\D/g,'').slice(-10); return NPA_TZ[d.slice(0,3)] || 'America/New_York'; }\nvar out = [];\nfor (var item of $input.all()) {\n var lead = $('Has Website?').item.json;\n var tz = npaTz(lead.phone);\n var h = 12, m = 0;\n try {\n var parts = new Intl.DateTimeFormat('en-US', {hour:'numeric', minute:'numeric', hour12:false, timeZone:tz}).formatToParts(new Date());\n for (var i = 0; i < parts.length; i++) {\n if (parts[i].type === 'hour') h = Number(parts[i].value);\n if (parts[i].type === 'minute') m = Number(parts[i].value);\n }\n } catch(e) {}\n var total_min = h * 60 + m;\n var within_window = total_min >= START_MIN && total_min < END_MIN;\n out.push({ json: Object.assign({}, item.json, { within_window: within_window, local_hour: h, local_minute: m, tz_used: tz }) });\n}\nreturn out;\n"
},
"id": "code-tcpa-check",
"name": "TCPA Time Check",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
832,
320
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "c-tcpa",
"leftValue": "={{ $json.within_window }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-tcpa-ok",
"name": "Within TCPA Window?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1024,
320
]
},
{
"parameters": {
"method": "POST",
"url": "YOUR_SLACK_WEBHOOK",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ text: '\u23f0 *TCPA SKIPPED* \u00b7 ' + ($('Has Website?').item.json.business_name || '?') + ' \u00b7 local hour ' + ($json.local_hour || '?') + ' ' + ($json.tz_used || '?') + ' outside 8am-9pm window' }) }}",
"options": {}
},
"id": "slack-tcpa",
"name": "Slack: TCPA Skipped",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1312,
336
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "c-sms",
"leftValue": "={{ !!$json.id }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "if-sms-sent",
"name": "SMS Sent OK?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1664,
-224
]
},
{
"parameters": {
"method": "POST",
"url": "YOUR_SLACK_WEBHOOK",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ text: '\u274c *OPENER FAILED* \u00b7 ' + ($('Has Website?').item.json.business_name || '?') + ' \u00b7 phone ' + ($('Has Website?').item.json.phone || '?') + ' \u00b7 error: ' + (($json.error && $json.error.message) || 'unknown') }) }}",
"options": {}
},
"id": "slack-sms-fail",
"name": "Slack: SMS Failed (opener)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1616,
240
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"maxItems": 100
},
"type": "n8n-nodes-base.limit",
"typeVersion": 1,
"position": [
160,
0
],
"id": "0ff705b7-a44a-4669-ba6b-8f041cdd3b43",
"name": "Limit"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 2
},
"conditions": [
{
"id": "c-close-ok",
"leftValue": "={{ $json.id }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "4b5c456e-fb84-4bbf-9e11-4a785e740d8f",
"name": "Close Create OK?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1200,
32
]
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT/update-status",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ id: $('Has Website?').item.json.id, status: 'dup_in_close' }) }}",
"options": {}
},
"id": "f1642180-5dbe-49d1-90d2-d41c8082f55d",
"name": "Convex /update-status (dup)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
480,
480
],
"onError": "continueRegularOutput"
}
],
"connections": {
"Daily 7:30/8:30/9:30/10:30 AM ET": {
"main": [
[
{
"node": "GET Convex /daily-batch",
"type": "main",
"index": 0
}
]
]
},
"GET Convex /daily-batch": {
"main": [
[
{
"node": "Limit",
"type": "main",
"index": 0
}
]
]
},
"Split to items": {
"main": [
[
{
"node": "Assign Senders (AI1..AI11, 100/day)",
"type": "main",
"index": 0
}
]
]
},
"Assign Senders (AI1..AI11, 100/day)": {
"main": [
[
{
"node": "Loop Batches (10)",
"type": "main",
"index": 0
}
]
]
},
"Loop Batches (10)": {
"main": [
[
{
"node": "Slack: send summary",
"type": "main",
"index": 0
}
],
[
{
"node": "Has Website?",
"type": "main",
"index": 0
}
]
]
},
"Has Website?": {
"main": [
[
{
"node": "Convex /flag-website (skip)",
"type": "main",
"index": 0
}
],
[
{
"node": "Close Duplicate Check",
"type": "main",
"index": 0
}
]
]
},
"Convex /flag-website (skip)": {
"main": [
[
{
"node": "Wait 1.5s (rate-limit)",
"type": "main",
"index": 0
}
]
]
},
"Close Duplicate Check": {
"main": [
[
{
"node": "Not Duplicate?",
"type": "main",
"index": 0
}
]
]
},
"Not Duplicate?": {
"main": [
[
{
"node": "TCPA Time Check",
"type": "main",
"index": 0
}
],
[
{
"node": "Convex /update-status (dup)",
"type": "main",
"index": 0
}
]
]
},
"Slack: Duplicate Skipped": {
"main": [
[
{
"node": "Wait 1.5s (rate-limit)",
"type": "main",
"index": 0
}
]
]
},
"TCPA Time Check": {
"main": [
[
{
"node": "Within TCPA Window?",
"type": "main",
"index": 0
}
]
]
},
"Within TCPA Window?": {
"main": [
[
{
"node": "Close POST /lead/",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack: TCPA Skipped",
"type": "main",
"index": 0
}
]
]
},
"Slack: TCPA Skipped": {
"main": [
[
{
"node": "Wait 1.5s (rate-limit)",
"type": "main",
"index": 0
}
]
]
},
"Convex /mark-pushed": {
"main": [
[
{
"node": "Close SMS \u2014 Opener (Text 1)",
"type": "main",
"index": 0
}
]
]
},
"Close SMS \u2014 Opener (Text 1)": {
"main": [
[
{
"node": "SMS Sent OK?",
"type": "main",
"index": 0
}
]
]
},
"SMS Sent OK?": {
"main": [
[
{
"node": "Convex /record-sms",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack: SMS Failed (opener)",
"type": "main",
"index": 0
}
]
]
},
"Slack: SMS Failed (opener)": {
"main": [
[
{
"node": "Wait 1.5s (rate-limit)",
"type": "main",
"index": 0
}
]
]
},
"Convex /record-sms": {
"main": [
[
{
"node": "Convex /sms-activities/log (opener)",
"type": "main",
"index": 0
}
]
]
},
"Convex /sms-activities/log (opener)": {
"main": [
[
{
"node": "Close PUT cf SMS Count",
"type": "main",
"index": 0
}
]
]
},
"Close PUT cf SMS Count": {
"main": [
[
{
"node": "Wait 1.5s (rate-limit)",
"type": "main",
"index": 0
}
]
]
},
"Wait 1.5s (rate-limit)": {
"main": [
[
{
"node": "Loop Batches (10)",
"type": "main",
"index": 0
}
]
]
},
"Limit": {
"main": [
[
{
"node": "Split to items",
"type": "main",
"index": 0
}
]
]
},
"Close POST /lead/": {
"main": [
[
{
"node": "Close Create OK?",
"type": "main",
"index": 0
}
]
]
},
"Close Create OK?": {
"main": [
[
{
"node": "Convex /mark-pushed",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait 1.5s (rate-limit)",
"type": "main",
"index": 0
}
]
]
},
"Convex /update-status (dup)": {
"main": [
[
{
"node": "Slack: Duplicate Skipped",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"timezone": "America/New_York",
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false,
"binaryMode": "separate",
"saveDataSuccessExecution": "all",
"saveDataErrorExecution": "all",
"saveManualExecutions": true
},
"active": false
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
WF-NW-02 — Daily 500 → Close + First SMS. Uses httpRequest. Scheduled trigger; 26 nodes.
Source: https://github.com/rafiulislam4246/auto-website-sell-workflows/blob/main/workflows/02-daily-batch-first-sms.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Birthday Automation - Production (Fixed). Uses stopAndError, httpRequest, emailSend, bannerbear. Scheduled trigger; 86 nodes.
This template runs two scheduled workflows to govern Microsoft Entra ID (Azure AD) guest accounts by detecting stale users via Microsoft Graph, staging deletions in SharePoint with a 72-hour window, n
Jira-Allure-Auto-Qa. Uses httpRequest, jira. Scheduled trigger; 68 nodes.
Spotify-Sync-Surrealdb-V1. Uses httpRequest, n8n-nodes-surrealdb, spotify. Scheduled trigger; 62 nodes.
As n8n instances scale, teams often lose track of sub-workflows—who uses them, where they are referenced, and whether they can be safely updated. This leads to inefficiencies like unnecessary copies o