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": "Ghost Rider CRM Import (Lead Processor)",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 6 * * 0"
}
]
}
},
"id": "trigger-weekly",
"name": "Weekly Sunday 6AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3,
"position": [
-768,
400
]
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/ghost-rider/leads-for-crm",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\n \"limit\": 200\n}",
"options": {
"timeout": 30000
}
},
"id": "fetch-tier-configs",
"name": "Fetch Pending Leads",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
-560,
400
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "// Extract Ghost Rider leads from Convex response\nvar response = $input.first().json;\nvar leads = response.leads || [];\n\nif (leads.length === 0) {\n return [{ json: { noLeads: true, message: 'No Ghost Rider leads ready for CRM' } }];\n}\n\nfunction titleCase(s) {\n if (!s) return '';\n return s.split(' ').map(function(word) {\n if (!word) return '';\n // Keep numbers and short words like 'AVE', 'ST' properly cased\n if (/^\\d/.test(word)) return word; // starts with number, keep as-is\n return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();\n }).join(' ');\n}\n\nconsole.log('Found ' + leads.length + ' Ghost Rider leads to process');\n\nreturn leads.map(function(lead) {\n return {\n json: {\n _id: lead.parcelId,\n parcelId: lead.parcelId,\n firstName: titleCase(lead.firstName),\n lastName: titleCase(lead.lastName),\n phone: lead.phone,\n address: titleCase(lead.street),\n city: titleCase(lead.city || 'Cleveland'),\n state: lead.state || 'OH',\n zip: lead.zip || '',\n county: 'Cuyahoga',\n distressScore: lead.distressScore || 0,\n primaryDistress: (lead.primaryDistress || '').toLowerCase().replace(/\\s+/g, '_'),\n listingType: 'BRRRR',\n tier: 'Ghost Rider',\n tierName: 'Ghost Rider',\n action: lead.action || 'call',\n email: null\n }\n };\n});"
},
"id": "prepare-tiers",
"name": "Extract Leads Array",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-384,
400
]
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "check-has-leads",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.noLeads }}"
}
]
},
"options": {}
},
"id": "check-has-leads",
"name": "Has Leads?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
144,
432
]
},
{
"parameters": {
"url": "=https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/dnc-check?phone={{ encodeURIComponent($('Has Leads?').item.json.phone) }}",
"options": {
"response": {
"response": {}
},
"timeout": 10000
}
},
"id": "dnc-check",
"name": "DNC Check",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
320,
432
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "check-not-dnc",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.isDnc }}"
}
]
},
"options": {}
},
"id": "check-has-phones",
"name": "Not on DNC?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
496,
432
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.close.com/api/v1/lead/",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"name\": \"{{ $('Prepare Lead Data').item.json._closeCRM.name }}\",\n \"status_id\": \"stat_jvO29nNSfLjAGfLvZmMRGKYheyxjjHoMha0R0RyJdEX\",\n \"contacts\": [{\n \"name\": \"{{ $('Prepare Lead Data').item.json._closeCRM.name }}\",\n \"phones\": [{\"phone\": \"{{ $('Prepare Lead Data').item.json.phone }}\", \"type\": \"mobile\"}],\n \"emails\": {{ $('Prepare Lead Data').item.json.email ? '[{\"email\": \"' + $('Prepare Lead Data').item.json.email + '\", \"type\": \"office\"}]' : '[]' }}\n }],\n \"custom.cf_oYpMjF2AbqjJ9TTnn3BZgCNlductY17okbScR0MvrhD\": {{ $('Prepare Lead Data').item.json._closeCRM.address ? '\"' + $('Prepare Lead Data').item.json._closeCRM.address + '\"' : 'null' }},\n \"custom.cf_V5lJPTCII754eYehZUytaQVlT2AhErHmoNfCRTLrBYp\": {{ $('Prepare Lead Data').item.json._closeCRM.city ? '\"' + $('Prepare Lead Data').item.json._closeCRM.city + '\"' : 'null' }},\n \"custom.cf_saFSzrigpdmJraiY8Tj6PC2HYnieWXrxvlZ8ycUd3gp\": \"{{ $('Prepare Lead Data').item.json._closeCRM.state }}\",\n \"custom.cf_BT2tCruHpFRG8IyUXao1H5EcTMTpAYKVJHM3eTcSG7j\": \"{{ $('Prepare Lead Data').item.json._closeCRM.county }}\",\n \"custom.cf_ckQ2OJQdAlFvlSLw15sdQWaJEEUahGFoHSIrqQpn2FV\": {{ $('Prepare Lead Data').item.json._closeCRM.distressScore }},\n \"custom.cf_12xW1CjB254VZJlmVtb98gmE7WbhjoGBLOAht3D2iRA\": {{ $('Prepare Lead Data').item.json._closeCRM.primaryDistress ? '\"' + $('Prepare Lead Data').item.json._closeCRM.primaryDistress + '\"' : 'null' }},\n \"custom.cf_YmhOs0uDToP5c6cb2n5UNGu0NfOOaLSuANmpDe1gdS7\": \"BatchLeads\",\n \"custom.cf_4Ijque0ihbMXlzeS4bqWU8QR1vSW9Xw0RA4ZJJadCHL\": \"Seller\",\n \"custom.cf_2h4TV44lPusp8OneeqElCTYJfSJAvPDyrUQlv5Tk0Uu\": \"{{ $('Prepare Lead Data').item.json._closeCRM.listingType }}\",\n \"custom.cf_cbbW5VEBpOx8PTGcaYyFTxOd7vT47tjG4iFYq6VtrEe\": {{ $('Prepare Lead Data').item.json._closeCRM.tierName ? '\"' + $('Prepare Lead Data').item.json._closeCRM.tierName + '\"' : 'null' }},\n \"custom.cf_POWtXhMQwwrBsrlkocDvtA5Fs57DvdKjwomTTgTP5vW\": \"Blitz\",\n \"custom.cf_cu1deNsnBPkD3h2sdAnxJbFcuXOX6zyCD4JEsEuVF7B\": \"GR\",\n \"custom.cf_4k0rrTQ6pQUytyLp3T0lAthZ7AEFUKyRbndn59Hsu8U\": 1,\n \"custom.cf_dNr5LbxwChSGZCGIH01fjAQxQ88Vk0CXhSuEQaLMF7h\": 8,\n \"custom.cf_QgUEmGIweiQG45RF5GD09JJNCyhcIZf2uNqOL8zT0sl\": \"Blitz\",\n \"custom.cf_IKdTtpAgEVychlXXkpLx9xVhd2wVAIrZN8bcw67AbiA\": {{ $('Prepare Lead Data').item.json._closeCRM.distressScore >= 80 ? '\"Urgent Call\"' : ($('Prepare Lead Data').item.json._closeCRM.distressScore >= 60 ? '\"Call\"' : ($('Prepare Lead Data').item.json._closeCRM.distressScore >= 50 ? '\"Text\"' : 'null')) }}\n}",
"options": {
"batching": {
"batch": {
"batchSize": 10
}
},
"timeout": 30000
}
},
"id": "create-close-lead",
"name": "Create Close CRM Lead",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1424,
448
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/ghost-rider/mark-in-crm",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"parcelId\": \"{{ $('Has Leads?').item.json.parcelId || '' }}\",\n \"closeLeadId\": \"{{ $json.closeLeadId || $('Log SMS & Mark Processed').item.json.closeLeadId || '' }}\"\n}",
"options": {
"timeout": 30000
}
},
"id": "log-to-convex",
"name": "Lead Processed",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
2864,
448
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "return [{\n json: {\n status: 'no_leads',\n message: 'No pending leads in Convex staging',\n processed: 0\n }\n}];"
},
"id": "handle-no-leads",
"name": "Handle No Leads",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
208,
112
]
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/ghost-rider/mark-in-crm",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"parcelId\": \"{{ $('Has Leads?').item.json.parcelId || '' }}\"\n}",
"options": {
"timeout": 10000
}
},
"id": "handle-dnc-filtered",
"name": "Mark as DNC Skipped",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
-48,
112
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "const allItems = $input.all().map(item => item.json);\n\nconst stats = {\n date: new Date().toISOString().split('T')[0],\n processed: 0,\n skippedDnc: 0,\n failed: 0,\n noLeads: false\n};\n\nfor (const item of allItems) {\n if (item.noLeads) {\n stats.noLeads = true;\n } else if (item.status === 'skipped_dnc') {\n stats.skippedDnc++;\n } else if (item.status === 'failed') {\n stats.failed++;\n } else if (item.success || item.smsSent) {\n stats.processed++;\n }\n}\n\nstats.total = stats.processed + stats.skippedDnc + stats.failed;\nstats.successRate = stats.total > 0 ? ((stats.processed / stats.total) * 100).toFixed(1) + '%' : '0%';\n\nreturn [{ json: stats }];"
},
"id": "aggregate-stats",
"name": "Aggregate Daily Stats",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-64,
-48
]
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/staged-leads-stats",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"date\": \"{{ $json.date }}\",\n \"processed\": {{ $json.processed }},\n \"skippedDnc\": {{ $json.skippedDnc }},\n \"failed\": {{ $json.failed }},\n \"total\": {{ $json.total }}\n}",
"options": {}
},
"id": "log-import-stats",
"name": "Log Daily Stats",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
112,
-48
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"id": "check-lead-created",
"operator": {
"type": "string",
"operation": "exists"
},
"leftValue": "={{ $json.id }}"
}
],
"combinator": "and"
},
"options": {}
},
"id": "check-create-success",
"name": "Lead Created?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1600,
448
]
},
{
"parameters": {
"jsCode": "// Ghost Rider - Fixed phone, Thompson Sampling templates\nvar CONVEX_URL = 'https://YOUR_CONVEX_DEPLOYMENT.convex.site';\nvar lead = $('Has Leads?').item.json;\nvar closeResponse = $('Create Close CRM Lead').item.json;\nvar assigned_phone_number = { phone: '+15555550100' };\nvar phone_source = 'ghost_rider_fixed';\nvar DISTRESS_MAP = {'pre_foreclosure':'Preforeclosure','preforeclosure':'Preforeclosure','probate':'Probate','inherited':'Probate','vacant':'Vacant','tax_delinquent':'Tax_Delinquent','tax_default':'Tax_Delinquent','lien':'Tax_Delinquent','absentee_owner':'Absentee','absentee':'Absentee','out_of_state':'Absentee','tired_landlord':'Absentee','high_equity':'High_Equity','senior_owner':'High_Equity','code_violation':'Code_Violation','code_violations':'Code_Violation'};\nvar listingType = 'BRRRR';\nvar distressType = null;\nvar distressSource = 'none';\nif (lead.primaryDistress && DISTRESS_MAP[lead.primaryDistress.toLowerCase()]) {\n distressType = DISTRESS_MAP[lead.primaryDistress.toLowerCase()];\n distressSource = 'primaryDistress';\n}\nvar template1_id = null;\nvar template1_variant = 'fallback';\nvar template1_source = 'fallback';\nvar templateBody = null;\nvar fallbackTemplates = ['Hey, got your message about {street_name}. What did you mean?','Hey, someone gave me this number for {street_name}. That you?','Hey, {street_name} - call me back when you get this','Hey, {street_name} came up. You still have it?'];\ntry {\n var apiUrl = CONVEX_URL + '/api/template-select-thompson?position=1&listing_type=' + encodeURIComponent(listingType);\n if (distressType) apiUrl += '&distress_type=' + encodeURIComponent(distressType);\n var templateResp = await this.helpers.httpRequest({ method: 'GET', url: apiUrl, returnFullResponse: false });\n if (templateResp && templateResp.selected && templateResp.selected.body) {\n templateBody = templateResp.selected.body;\n template1_id = templateResp.selected._id;\n template1_variant = templateResp.selected.variant_name || 'thompson';\n template1_source = 'thompson_sampling';\n } else { throw new Error('No template returned'); }\n} catch (e) {\n templateBody = fallbackTemplates[Math.floor(Math.random() * fallbackTemplates.length)];\n template1_id = 'fallback_curiosity';\n}\nvar address = lead.address || 'your property';\nvar streetName = address.replace(/^\\d+\\s+/, '').split(',')[0].trim();\nvar sms_text = templateBody.replace(/\\{street_name\\}/gi, streetName).replace(/\\{street\\}/gi, streetName).replace(/\\{address\\}/gi, address).replace(/\\{city\\}/gi, lead.city || '').replace(/\\{firstName\\}/gi, lead.firstName || '').replace(/\\{first_name\\}/gi, lead.firstName || '').replace(/\\{\\{street_name\\}\\}/gi, streetName).replace(/\\{\\{street\\}\\}/gi, streetName).replace(/\\{\\{address\\}\\}/gi, address);\nvar now = new Date();\nvar tomorrow = new Date(now);\ntomorrow.setDate(tomorrow.getDate() + 1);\ntomorrow.setHours(9, 0, 0, 0);\nreturn [{json: {_id: lead._id, parcelId: lead.parcelId, stagedLeadId: lead._id, tier: lead.tier, tierName: lead.tierName, listingType: listingType, distressType: distressType, distressSource: distressSource, primaryDistress: lead.primaryDistress || 'none', closeLeadId: closeResponse.id, contactId: closeResponse.contacts && closeResponse.contacts[0] ? closeResponse.contacts[0].id : null, firstName: lead.firstName, lastName: lead.lastName, phone: lead.phone, address: lead.address, city: lead.city, state: lead.state || 'OH', zip: lead.zip, assigned_phone_number: assigned_phone_number, phone_source: phone_source, template1_id: template1_id, template1_variant: template1_variant, template1_source: template1_source, sms_text: sms_text, text1_sent: now.toISOString(), text2_scheduled: tomorrow.toISOString()}}];"
},
"id": "select-phone-template",
"name": "Select Phone & Template",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1792,
432
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.close.com/api/v1/activity/sms/",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"lead_id\": \"{{ $('Select Phone & Template').item.json.closeLeadId }}\",\n \"contact_id\": \"{{ $('Select Phone & Template').item.json.contactId }}\",\n \"local_phone\": \"{{ $('Select Phone & Template').item.json.assigned_phone_number.phone }}\",\n \"remote_phone\": \"{{ $('Select Phone & Template').item.json.phone }}\",\n \"text\": {{ JSON.stringify($('Select Phone & Template').item.json.sms_text) }},\n \"direction\": \"outbound\",\n \"status\": \"outbox\"\n}",
"options": {
"timeout": 30000
}
},
"id": "send-sms",
"name": "Send Text 1",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
2208,
272
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "PUT",
"url": "=https://api.close.com/api/v1/lead/{{ $('Select Phone & Template').item.json.closeLeadId }}/",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"custom.cf_sEleJ5F3wkFxcVRQTN8Rvjn98wC5TRf3IbKeekHC2Zx\": \"{{ $('Select Phone & Template').item.json.assigned_phone_number.phone }}\",\n \"custom.cf_hZRaLyj0UmOrFbgNbWN2lKMkpzRTrNAIcIzySHw18af\": \"{{ $('Select Phone & Template').item.json.text1_sent }}\",\n \"custom.cf_yScuWVXn4Xs6JOSrzFSQQy279bmkgttywJ0P4ub8NeV\": \"{{ $('Select Phone & Template').item.json.text2_scheduled }}\",\n \"custom.cf_mLLAy4ctJLqn2zhjECML593u8vs4lsIDtMjTQpHudtf\": \"{{ $('Select Phone & Template').item.json.text1_sent }}\",\n \"custom.cf_d8OZm5MmRSchNbeJF4KXmrAm3JpjVscpyMKrAQ6ek9r\": \"{{ $('Select Phone & Template').item.json.text2_scheduled }}\",\n \"custom.cf_vrVajMz5zvcWRSxoflOq1AbfxWT12lVCvQZD04hIPvG\": \"outbound\",\n \"custom.cf_POWtXhMQwwrBsrlkocDvtA5Fs57DvdKjwomTTgTP5vW\": \"Blitz\",\n \"custom.cf_QgUEmGIweiQG45RF5GD09JJNCyhcIZf2uNqOL8zT0sl\": \"Blitz\"\n}",
"options": {
"timeout": 30000
}
},
"id": "update-lead-fields",
"name": "Update Lead Fields",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
2432,
272
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "// Log SMS to Convex - Ghost Rider (no phone pool rotation)\nvar CONVEX_URL = 'https://YOUR_CONVEX_DEPLOYMENT.convex.site';\nvar selectData = $('Select Phone & Template').item.json;\nvar smsResponse = $('Send Text 1').item.json;\ntry {\n await this.helpers.httpRequest({method: 'POST', url: CONVEX_URL + '/api/conversation-log', body: {lead_id: selectData.closeLeadId, phone: selectData.phone, direction: 'outbound', message_text: selectData.sms_text, template_id: selectData.template1_id || null, close_activity_id: smsResponse.id || null}, json: true, returnFullResponse: false});\n} catch (e) { console.log('Failed to log SMS:', e.message); }\nreturn [{json: {success: true, closeLeadId: selectData.closeLeadId, stagedLeadId: selectData.stagedLeadId, parcelId: selectData.parcelId, tier: selectData.tier, phone: selectData.phone, firstName: selectData.firstName, address: selectData.address, city: selectData.city, zip: selectData.zip, listingType: selectData.listingType, assigned_phone_number: selectData.assigned_phone_number, template1_id: selectData.template1_id, template1_variant: selectData.template1_variant, template1_source: selectData.template1_source, text2_scheduled: selectData.text2_scheduled, smsSent: true}}];"
},
"id": "log-sms-convex",
"name": "Log SMS & Mark Processed",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2624,
272
]
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/ghost-rider/mark-in-crm",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"parcelId\": \"{{ $('Has Leads?').item.json.parcelId || '' }}\"\n}",
"options": {
"timeout": 10000
}
},
"id": "handle-create-failed",
"name": "Mark as Failed",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1408,
640
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/follow-up-queue-add",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"lead_id\": \"{{ $json.closeLeadId }}\",\n \"phone\": \"{{ $json.phone }}\",\n \"first_name\": \"{{ $json.firstName }}\",\n \"address\": \"{{ $json.address }}\",\n \"city\": \"{{ $json.city }}\",\n \"zip\": \"{{ $json.zip }}\",\n \"next_send_date\": {{ new Date($json.text2_scheduled).getTime() }},\n \"sequence_position\": 2,\n \"lead_type\": \"Seller\",\n \"listing_type\": \"{{ $json.listingType || 'BRRRR' }}\",\n \"tier\": \"{{ $json.tier }}\",\n \"assigned_phone\": \"{{ $json.assigned_phone_number.phone }}\",\n \"template_id\": \"{{ $json.template1_id || '' }}\",\n \"status\": \"pending\"\n}",
"options": {
"timeout": 10000
}
},
"id": "add-follow-up-queue",
"name": "Add to Follow-Up Queue",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
3072,
272
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "const input = $input.first().json;\nconst now = new Date();\nconst estString = now.toLocaleString('en-US', { timeZone: 'America/New_York' });\nconst estDate = new Date(estString);\nconst currentHourEST = estDate.getHours();\nconst dayOfWeekEST = estDate.getDay();\n\nlet canSend = true;\nlet skipReason = '';\n\nif (dayOfWeekEST === 0) {\n canSend = false;\n skipReason = 'Sunday - no SMS allowed';\n} else if (dayOfWeekEST === 6) {\n if (currentHourEST < 10 || currentHourEST >= 18) {\n canSend = false;\n skipReason = 'Saturday outside 10AM-6PM EST';\n }\n} else {\n if (currentHourEST < 9 || currentHourEST >= 20) {\n canSend = false;\n skipReason = 'Outside 9AM-8PM EST window';\n }\n}\n\nreturn [{\n json: {\n ...input,\n tcpaCheck: { canSend, skipReason, checkedAt: now.toISOString(), currentHourEST, dayOfWeekEST }\n }\n}];"
},
"id": "tcpa-check-daily",
"name": "TCPA Time Check",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2000,
432
]
},
{
"parameters": {
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"id": "tcpa-ok",
"leftValue": "={{ $json.tcpaCheck.canSend }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "tcpa-branch-daily",
"name": "Within Send Window?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
2192,
432
]
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/follow-up-queue-add",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"lead_id\": \"{{ $json.closeLeadId }}\",\n \"phone\": \"{{ $json.phone }}\",\n \"first_name\": \"{{ $json.firstName }}\",\n \"address\": \"{{ $json.address }}\",\n \"city\": \"{{ $json.city }}\",\n \"zip\": \"{{ $json.zip }}\",\n \"next_send_date\": {{ Date.now() + 3600000 }},\n \"sequence_position\": 1,\n \"lead_type\": \"Seller\",\n \"listing_type\": \"{{ $json.listingType || 'BRRRR' }}\",\n \"tier\": \"{{ $json.tier }}\",\n \"status\": \"pending\"\n}",
"options": {
"timeout": 10000
}
},
"id": "queue-for-later-daily",
"name": "Queue for Later (TCPA)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
2432,
448
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "const input = $('TCPA Time Check').item.json;\n\nreturn [{\n json: {\n success: true,\n closeLeadId: input.closeLeadId,\n stagedLeadId: input.stagedLeadId,\n tier: input.tier,\n smsSent: false,\n tcpaDeferred: true,\n reason: input.tcpaCheck?.skipReason || 'Outside TCPA window'\n }\n}];"
},
"id": "log-tcpa-skip-daily",
"name": "Log TCPA Skip",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2640,
448
]
},
{
"parameters": {
"maxItems": 200
},
"type": "n8n-nodes-base.limit",
"typeVersion": 1,
"position": [
-384,
224
],
"id": "limit-node",
"name": "Limit"
},
{
"parameters": {
"url": "=https://api.close.com/api/v1/lead/?query=phone%3A{{ encodeURIComponent($('Has Leads?').item.json.phone) }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"options": {
"timeout": 15000
}
},
"id": "check-close-dup",
"name": "Check Close Duplicate",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1072,
432
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "check-no-results",
"operator": {
"type": "number",
"operation": "equals"
},
"leftValue": "={{ $json.data ? $json.data.length : 0 }}",
"rightValue": 0
}
]
},
"options": {}
},
"id": "if-not-duplicate",
"name": "Not Duplicate?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
1072,
272
]
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/ghost-rider/mark-in-crm",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"parcelId\": \"{{ $('Has Leads?').item.json.parcelId || '' }}\"\n}",
"options": {
"timeout": 10000
}
},
"id": "mark-duplicate",
"name": "Mark as Duplicate",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1600,
640
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"options": {}
},
"id": "loop-leads",
"name": "Loop Each Lead",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [
-48,
416
]
},
{
"parameters": {
"url": "=https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/dedup-check?address={{ encodeURIComponent($('Has Leads?').item.json.address) }}&zip={{ encodeURIComponent($('Has Leads?').item.json.zip) }}&days=90",
"options": {
"timeout": 10000
}
},
"id": "address-dedup-check",
"name": "Address Dedup Check",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
736,
416
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "check-not-address-dup",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.isDuplicate }}"
}
]
},
"options": {}
},
"id": "not-address-dup",
"name": "Not Address Duplicate?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
864,
432
]
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/ghost-rider/mark-in-crm",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"parcelId\": \"{{ $('Has Leads?').item.json.parcelId || '' }}\"\n}",
"options": {
"timeout": 10000
}
},
"id": "mark-address-dup",
"name": "Mark as Address Duplicate",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
864,
592
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"content": "## BatchData Import (Weekly Lead Processor)\n\n**Purpose:** Process new seller leads from Convex staging into Close CRM and send initial Text 1 (Curiosity Message)\n\n**Schedule:** Weekly on Sunday at 6AM EST\n- Allows leads to accumulate during the week\n- Processes up to 750 leads per run\n- Manual trigger available for testing\n\n**Data Flow:**\n```\nConvex Staging \u2192 DNC Check \u2192 Address Dedup \u2192 Close Dedup \u2192 Create Lead \u2192 Send SMS \u2192 Follow-Up Queue\n```\n\n**Key Features:**\n- \u2705 DNC Registry compliance\n- \u2705 90-day address deduplication\n- \u2705 Close CRM duplicate detection\n- \u2705 Thompson Sampling for template selection\n- \u2705 TCPA time window enforcement\n- \u2705 Distress-aware template matching\n\n**Convex Base URL:** `https://YOUR_CONVEX_DEPLOYMENT.convex.site`",
"height": 480,
"width": 380,
"color": 4
},
"id": "sticky-overview",
"name": "\ud83d\udccb Workflow Overview",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-576,
-352
]
},
{
"parameters": {
"content": "## Lead Validation Pipeline\n\n**Step 1: DNC Check**\n`GET /api/dnc-check?phone={phone}`\n- Checks Convex DNC registry\n- Returns `{ isDnc: true/false }`\n- If DNC \u2192 Mark as skipped, return to loop\n\n**Step 2: Address Deduplication**\n`GET /api/dedup-check?address={addr}&zip={zip}&days=90`\n- Prevents re-contacting same property within 90 days\n- Returns `{ isDuplicate: true/false }`\n\n**Step 3: Close CRM Duplicate Check**\n`GET /api/v1/lead/?query=phone:{phone}`\n- Queries Close CRM for existing lead with same phone\n- If found \u2192 Mark as duplicate, skip creation\n\n**Skip Reasons Logged:**\n- `dnc` - Phone on Do Not Call list\n- `address_duplicate` - Property contacted in last 90 days\n- `duplicate` - Lead already exists in Close CRM\n- `error` - Failed to create lead",
"height": 480,
"width": 380,
"color": 2
},
"id": "sticky-validation",
"name": "\ud83d\udd0d Lead Validation Pipeline",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
944,
-432
]
},
{
"parameters": {
"content": "## Close CRM Integration\n\n**Create Lead with Custom Fields:**\n- Pipeline: `Seller`\n- Stage: `Blitz`\n- Phase: `Blitz`\n- Position: `1` (of 8-touch sequence)\n- Market Type: `BRRRR` or `ARV`\n- Source: `BatchLeads`\n\n**Phone Selection (Thompson Sampling):**\n`GET /api/phone-select`\n- Weighted random selection from phone pool\n- Fallback: `+15555550100`, `+15555550100`\n\n**Template Selection:**\n`GET /api/template-select-thompson?position=1&listing_type={type}`\n- Thompson Sampling (Multi-Armed Bandit)\n- Distress-aware: Preforeclosure, Vacant, Tax_Delinquent, etc.\n- Text 1 = Curiosity message with `{street_name}`\n\n**SMS via Close CRM:**\n`POST /api/v1/activity/sms/`\n- Direction: outbound\n- Status: outbox (queued for sending)\n\n**After SMS:**\n- Update lead: Stage=Blitz, Status=Text 1 Sent\n- Log to Convex conversation_history\n- Add to follow-up queue for Text 2-8",
"height": 520,
"width": 400,
"color": 5
},
"id": "sticky-close-crm",
"name": "\ud83d\udcf1 Close CRM & SMS Flow",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1408,
-448
]
},
{
"parameters": {
"content": "## TCPA Time Windows (EST)\n\n**Weekdays (Mon-Fri):**\n- \u2705 9:00 AM - 8:00 PM EST\n\n**Saturday:**\n- \u2705 10:00 AM - 6:00 PM EST\n\n**Sunday:**\n- \u274c NO SMS ALLOWED\n\n**If Outside Window:**\n- SMS queued for later via `/api/follow-up-queue-add`\n- Lead marked as `tcpaDeferred: true`\n- Will be processed by Follow-Up Engine\n\n---\n\n## Follow-Up Queue\n\n`POST /api/follow-up-queue-add`\n- Schedules Text 2 for next day 9AM\n- Tracks: position, listing_type, assigned_phone\n- Processed by separate Follow-Up Engine workflow\n\n---\n\n## Slack Report\n\nChannel: `#test` (C0ABWBR67JL)\n- Total processed with Text 1 sent\n- Skipped (DNC count)\n- Failed count\n- Success rate percentage",
"height": 540,
"width": 340,
"color": 6
},
"id": "sticky-tcpa",
"name": "\u23f0 TCPA Compliance & Reporting",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1904,
-448
]
},
{
"parameters": {
"amount": 7
},
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
3072,
448
],
"id": "7e6fe925-d881-4501-8ed2-394bf2941f66",
"name": "Wait"
},
{
"parameters": {
"jsCode": "// PREPARE LEAD DATA - Tier lookup + null handling for Close CRM\nvar CONVEX_URL = 'https://YOUR_CONVEX_DEPLOYMENT.convex.site';\nvar lead = $('Has Leads?').item.json;\n\n// Get current tier info\nvar tier = lead.tier || '';\nvar tierName = lead.tierName || '';\nvar listingType = lead.listingType || '';\n\n// If tier is empty and we have a ZIP, try to lookup from tier_configs\nif (!tier && lead.zip) {\n try {\n var tierResp = await this.helpers.httpRequest({\n method: 'GET',\n url: CONVEX_URL + '/api/tier-configs/by-zip?zip=' + encodeURIComponent(lead.zip),\n returnFullResponse: false\n });\n \n if (tierResp && tierResp.tier) {\n tier = tierResp.tier;\n tierName = tierResp.name || tierResp.tierName || tier;\n listingType = tierResp.listing_type || tierResp.listingType || 'BRRRR';\n console.log('Tier lookup success: ' + tier + ' (' + tierName + ') for ZIP ' + lead.zip);\n }\n } catch (e) {\n console.log('Tier lookup failed:', e.message);\n }\n}\n\n// Default tier if still empty\nif (!tier) {\n tier = 'General';\n tierName = 'General Leads';\n listingType = 'BRRRR';\n console.log('Using default tier: General');\n}\n\n// Default listing type\nif (!listingType) {\n listingType = 'BRRRR';\n}\n\n// Helper to convert empty strings to null for Close CRM\nvar nullIfEmpty = function(val) { return (val && val.trim && val.trim() !== '') ? val : null; };\n\n// Normalize distress type for Close CRM\nvar DISTRESS_NORMALIZE = {\n 'code_violations': 'code_violation',\n 'code_violation': 'code_violation',\n 'tax_delinquent': 'tax_delinquent',\n 'tax_default': 'tax_delinquent',\n 'vacant': 'vacant',\n 'pre_foreclosure': 'pre_foreclosure',\n 'preforeclosure': 'pre_foreclosure',\n 'probate': 'probate',\n 'inherited': 'probate',\n 'absentee_owner': 'absentee_owner',\n 'absentee': 'absentee_owner',\n 'out_of_state': 'out_of_state',\n 'tired_landlord': 'absentee_owner',\n 'lien': 'lien',\n 'high_equity': 'high_equity',\n 'senior_owner': 'senior_owner'\n};\nvar rawDistress = (lead.primaryDistress || '').toLowerCase().replace(/\\s+/g, '_');\nvar normalizedDistress = DISTRESS_NORMALIZE[rawDistress] || rawDistress || null;\n// Only null out if truly empty\nif (normalizedDistress === '') normalizedDistress = null;\n\n// Prepare the lead data with null handling\nreturn [{\n json: {\n // Original lead data\n ...lead,\n // Override with resolved tier info\n tier: tier,\n tierName: tierName,\n listingType: listingType,\n // Prepared Close CRM fields (null if empty)\n _closeCRM: {\n name: ((lead.firstName || '') + ' ' + (lead.lastName || '')).trim() || 'Unknown',\n address: nullIfEmpty(lead.address),\n city: nullIfEmpty(lead.city),\n state: lead.state || 'OH',\n county: nullIfEmpty(lead.county) || 'Cuyahoga',\n tierName: nullIfEmpty(tierName),\n listingType: listingType === 'HIGH_ARV' || listingType === 'High_ARV' ? 'ARV' : 'BRRRR',\n primaryDistress: normalizedDistress,\n distressScore: lead.distressScore || 0\n }\n }\n}];"
},
"id": "prepare-lead-data",
"name": "Prepare Lead Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1424,
272
]
},
{
"parameters": {
"method": "POST",
"url": "=https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/financial-event",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"event_type\": \"sms_cost\",\n \"amount\": 0.02,\n \"currency\": \"USD\",\n \"lead_id\": \"{{ $json.lead_id || $json.leadId || '' }}\",\n \"description\": \"SMS sent via Instantly\",\n \"metadata\": {\n \"workflow\": \"BatchData Import\",\n \"phone\": \"{{ $json.phone || '' }}\",\n \"template_id\": \"{{ $json.template_id || '' }}\"\n }\n}",
"options": {}
},
"id": "log-sms-cost",
"name": "Log SMS Cost",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2432,
112
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/log-lead-event",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"event_type\": \"live\",\n \"workflow\": \"batchdata_import\",\n \"lead_id\": \"{{ $('Log SMS & Mark Processed').item.json.closeLeadId || '' }}\",\n \"phone\": \"{{ $('Log SMS & Mark Processed').item.json.phone || '' }}\",\n \"address\": \"{{ $('Log SMS & Mark Processed').item.json.address || '' }}\",\n \"tier\": \"{{ $('Log SMS & Mark Processed').item.json.tier || '' }}\"\n}",
"options": {}
},
"id": "log-pipeline-live",
"name": "Log Pipeline: Live",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3056,
80
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_CONVEX_DEPLOYMENT.convex.site/api/log-sms-sent",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"workflow\": \"batchdata_import\",\n \"text_position\": 1,\n \"lead_id\": \"{{ $('Create Close CRM Lead').item.json.id || '' }}\",\n \"phone\": \"{{ $('Select Phone & Template').item.json.phone || '' }}\",\n \"address\": \"{{ $('Select Phone & Template').item.json.address || '' }}\",\n \"template_id\": \"{{ $('Select Phone & Template').item.json.template1_id || '' }}\"\n}",
"options": {}
},
"id": "log-sms-sent-text1",
"name": "Log SMS Sent (Text 1)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2144,
128
],
"onError": "continueRegularOutput"
}
],
"connections": {
"Weekly Sunday 6AM": {
"main": [
[]
]
},
"Fetch Pending Leads": {
"main": [
[
{
"node": "Extract Leads Array",
"type": "main",
"index": 0
}
]
]
},
"Extract Leads Array": {
"main": [
[
{
"node": "Limit",
"type": "main",
"index": 0
}
]
]
},
"Limit": {
"main": [
[
{
"node": "Loop Each Lead",
"type": "main",
"index": 0
}
]
]
},
"Has Leads?": {
"main": [
[
{
"node": "DNC Check",
"type": "main",
"index": 0
}
],
[
{
"node": "Handle No Leads",
"type": "main",
"index": 0
}
]
]
},
"DNC Check": {
"main": [
[
{
"node": "Not on DNC?",
"type": "main",
"index": 0
}
]
]
},
"Not on DNC?": {
"main": [
[
{
"node": "Address Dedup Check",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark as DNC Skipped",
"type": "main",
"index": 0
}
]
]
},
"Lead Created?": {
"main": [
[
{
"node": "Select Phone & Template",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark as Failed",
"type": "main",
"index": 0
}
]
]
},
"Select Phone & Template": {
"main": [
[
{
"node": "TCPA Time Check",
"type": "main",
"index": 0
}
]
]
},
"TCPA Time Check": {
"main": [
[
{
"node": "Within Send Window?",
"type": "main",
"index": 0
}
]
]
},
"Within Send Window?": {
"main": [
[
{
"node": "Send Text 1",
"type": "main",
"index": 0
}
],
[
{
"node": "Queue for Later (TCPA)",
"type": "main",
"index": 0
}
]
]
},
"Send Text 1": {
"main": [
[
{
"node": "Update Lead Fields",
"type": "main",
"index": 0
},
{
"node": "Log SMS Cost",
"type": "main",
"index": 0
},
{
"node": "Log SMS Sent (Text 1)",
"type": "main",
"index": 0
}
]
]
},
"Update Lead Fields": {
"main": [
[
{
"node": "Log SMS & Mark Processed",
"type": "main",
"index": 0
}
]
]
},
"Log SMS & Mark Processed": {
"main": [
[
{
"node": "Add to Follow-Up Queue",
"type": "main",
"index": 0
}
]
]
},
"Add to Follow-Up Queue": {
"main": [
[
{
"node": "Lead Processed",
"type": "main",
"index": 0
},
{
"node": "Log Pipeline: Live",
"type": "main",
"index": 0
}
]
]
},
"Lead Processed": {
"main": [
[
{
"node": "Wait",
"type": "main",
"index": 0
}
]
]
},
"Queue for Later (TCPA)": {
"main": [
[
{
"node": "Log TCPA Skip",
"type": "main",
"index": 0
}
]
]
},
"Log TCPA Skip": {
"main": [
[
{
"node": "Lead Processed",
"type": "main",
"index": 0
}
]
]
},
"Handle No Leads": {
"main": [
[
{
"node": "Loop Each Lead",
"type": "main",
"index": 0
}
]
]
},
"Mark as DNC Skipped": {
"main": [
[
{
"node": "Loop Each Lead",
"type": "main",
"index": 0
}
]
]
},
"Mark as Failed": {
"main": [
[
{
"node": "Loop Each Lead",
"type": "main",
"index": 0
}
]
]
},
"Aggregate Daily Stats": {
"main": [
[
{
"node": "Log Daily Stats",
"type": "main",
"index": 0
}
]
]
},
"Log Daily Stats": {
"main": [
[]
]
},
"Create Close CRM Lead": {
"main": [
[
{
"node": "Lead Created?",
"type": "main",
"index": 0
}
]
]
},
"Check Close Duplicate": {
"main": [
[
{
"node": "Not Duplicate?",
"type": "main",
"index": 0
}
]
]
},
"Not Duplicate?": {
"main": [
[
{
"node": "Prepare Lead Data",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark as Duplicate",
"type": "main",
"index": 0
}
]
]
},
"Mark as Duplicate": {
"main": [
[
{
"node": "Loop Each Lead",
"type": "main",
"index": 0
}
]
]
},
"Loop Each Lead": {
"main": [
[
{
"node": "Aggregate Daily Stats",
"type": "main",
"index": 0
}
],
[
{
"node": "Has Leads?",
"type": "main",
"index": 0
}
]
]
},
"Address Dedup Check": {
"main": [
[
{
"node": "Not Address Duplicate?",
"type": "main",
"index": 0
}
]
]
},
"Not Address Duplicate?": {
"main": [
[
{
"node": "Check Close Duplicate",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark as Address Duplicate",
"type": "main",
"index": 0
}
]
]
},
"Mark as Address Duplicate": {
"main": [
[
{
"node": "Loop Each Lead",
"type": "main",
"index": 0
}
]
]
},
"Wait": {
"main": [
[
{
"node": "Loop Each Lead",
"type": "main",
"index": 0
}
]
]
},
"Prepare Lead Data": {
"main": [
[
{
"node": "Create Close CRM Lead",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false,
"binaryMode": "separate"
},
"active": false
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Ghost Rider CRM Import (Lead Processor). Uses httpRequest. Scheduled trigger; 40 nodes.
Source: https://github.com/rafiulislam4246/real-estate-acquisition-workflows/blob/main/workflows/02-ghost-rider-crm-import.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.
Workflow A — WhatsApp Lead Intake & Qualification. Uses postgres, httpRequest, errorTrigger. Scheduled trigger; 67 nodes.
Build authentic Reddit presence and generate qualified leads through AI-powered community engagement that provides genuine value without spam or promotion.
This workflow runs on scheduled weekly and monthly triggers to generate unified marketing performance reports. It processes multiple websites by collecting analytics data, paid ads performance, and CR
Fetch Multiple Google Analytics GA4 metrics daily, post to Discord, update previous day’s entry as GA data finalizes over seven days. Automates daily traffic reporting Maintains single message per day
WABA Message Journey Flow Documentation This document outlines the automated workflow for sending WhatsApp messages to contacts, triggered hourly and managed through disposition and message count logi