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": "DSB Sales Late Sweep",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 7,13,20 * * 1-6"
}
]
}
},
"id": "ls-trigger",
"name": "Schedule (7am, 1pm, 8pm CST)",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
-2400,
800
]
},
{
"parameters": {
"jsCode": "// Compute past N business days (excluding weekends) ending today (CST).\nconst N = 7;\nconst central = new Date().toLocaleDateString('en-CA', { timeZone: 'America/Chicago' });\nconst [y, m, d] = central.split('-').map(Number);\nconst end = new Date(y, m - 1, d);\nconst dates = [];\nconst cur = new Date(end);\nwhile (dates.length < N) {\n const day = cur.getDay();\n if (day !== 0 && day !== 6) {\n const ds = cur.getFullYear() + '-' + String(cur.getMonth() + 1).padStart(2, '0') + '-' + String(cur.getDate()).padStart(2, '0');\n dates.push(ds);\n }\n cur.setDate(cur.getDate() - 1);\n}\ndates.reverse();\nreturn [{ json: { backfillDates: dates } }];"
},
"id": "ls-dates",
"name": "Compute Backfill Dates",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-2160,
800
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.apify.com/v2/acts/MIlgC3KEFTXzIcnMt/runs?token=YOUR_APIFY_TOKEN",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ crmUsername: \"YOUR_CRM_USERNAME\", crmPassword: \"YOUR_CRM_PASSWORD\", salesBackfillDates: $json.backfillDates }) }}",
"options": {
"timeout": 10000
}
},
"id": "ls-start-apify",
"name": "Start Apify Backfill",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-1920,
800
]
},
{
"parameters": {
"amount": 90,
"unit": "seconds"
},
"id": "ls-wait1",
"name": "Wait 90s",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
-1680,
800
]
},
{
"parameters": {
"url": "=https://api.apify.com/v2/actor-runs/{{ $node['Start Apify Backfill'].json.data.id }}?token=YOUR_APIFY_TOKEN",
"options": {
"timeout": 10000
}
},
"id": "ls-poll",
"name": "Poll Run Status",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-1440,
800
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"id": "is-done",
"leftValue": "={{ $json.data.status }}",
"rightValue": "SUCCEEDED",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
}
},
"id": "ls-done",
"name": "Run Done?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-1200,
800
]
},
{
"parameters": {
"amount": 60,
"unit": "seconds"
},
"id": "ls-wait2",
"name": "Wait & Retry",
"type": "n8n-nodes-base.wait",
"typeVersion": 1.1,
"position": [
-1200,
1040
]
},
{
"parameters": {
"jsCode": "const runStatus = $input.first().json;\nconst datasetId = runStatus?.data?.defaultDatasetId;\nconst runId = runStatus?.data?.id || 'unknown';\nif (!datasetId) {\n return [{ json: { error: true, message: 'No defaultDatasetId. runId=' + runId } }];\n}\nconst token = 'YOUR_APIFY_TOKEN';\nconst url = `https://api.apify.com/v2/datasets/${datasetId}/items?token=${token}`;\nconst raw = await this.helpers.httpRequest({ method: 'GET', url, encoding: 'utf-8', returnFullResponse: false, timeout: 60000, headers: { Accept: 'application/json' } });\nlet items = raw;\nif (typeof raw === 'string') { try { items = JSON.parse(raw); } catch (e) { return [{ json: { error: true, message: 'JSON parse failed: ' + raw.substring(0,200) } }]; } }\nif (!Array.isArray(items) || items.length === 0) {\n return [{ json: { error: true, message: 'Empty dataset for runId=' + runId, datasetId } }];\n}\nconst backfills = items.filter((i) => i._type === 'sale_made_backfill');\nreturn [{ json: { backfills, runId, datasetId, count: backfills.length } }];"
},
"id": "ls-fetch",
"name": "Fetch Dataset",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-960,
800
]
},
{
"parameters": {
"conditions": {
"options": {
"typeValidation": "strict"
},
"conditions": [
{
"id": "has-data",
"leftValue": "={{ $json.error }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "false"
}
}
],
"combinator": "and"
}
},
"id": "ls-data-ok",
"name": "Data OK?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-720,
800
]
},
{
"parameters": {
"jsCode": "// Split each backfill item into its own n8n item so the next HTTP node fans out per date.\nconst backfills = $json.backfills || [];\nreturn backfills.map((b) => ({ json: { scrape_date: b.scrape_date, agents: b.agents || [], mode: 'sales_only' } }));"
},
"id": "ls-split",
"name": "Split Per Date",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-480,
720
]
},
{
"parameters": {
"method": "POST",
"url": "https://YOUR_SUPABASE_REF.supabase.co/functions/v1/ingest-daily-scrape",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Authorization",
"value": "Bearer YOUR_ROLI_ANON_KEY"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ scrape_date: $json.scrape_date, agents: $json.agents, mode: 'sales_only' }) }}",
"options": {
"timeout": 30000
}
},
"id": "ls-ingest",
"name": "Ingest sales_only",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-240,
720
]
},
{
"parameters": {
"mode": "combine",
"combineBy": "combineAll",
"options": {}
},
"id": "ls-merge",
"name": "Aggregate Results",
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
0,
720
]
},
{
"parameters": {
"jsCode": "// Build a Slack summary of all backfilled dates and any deltas detected.\nconst items = $input.all().map((i) => i.json);\nlet totalUpdated = 0;\nlet totalInserted = 0;\nlet totalAgents = 0;\nconst lines = [];\nfor (const r of items) {\n if (!r) continue;\n const date = r.scrape_date || 'unknown';\n const updated = r.sales_only_updated || 0;\n const inserted = r.sales_only_inserted || 0;\n const ups = r.upserted || 0;\n totalUpdated += updated;\n totalInserted += inserted;\n totalAgents += ups;\n lines.push(`\u2022 *${date}* \u2014 ${ups} agents (updated: ${updated}, inserted: ${inserted})`);\n}\nconst now = new Date();\nconst centralStr = now.toLocaleString('en-US', { timeZone: 'America/Chicago', hour: 'numeric', minute: '2-digit', hour12: true });\nconst message = {\n text: `DSB sales late sweep complete (${items.length} dates)`,\n blocks: [\n { type: 'header', text: { type: 'plain_text', text: ':arrows_counterclockwise: DSB Sales Late Sweep', emoji: true } },\n { type: 'section', text: { type: 'mrkdwn', text: `*Sweep run at ${centralStr} CST*\\n${items.length} dates re-scraped (Sale Made only)\\n*Total:* ${totalAgents} agents | ${totalUpdated} updated | ${totalInserted} inserted` } },\n { type: 'divider' },\n { type: 'section', text: { type: 'mrkdwn', text: lines.join('\\n') || '_No data_' } },\n { type: 'context', elements: [{ type: 'mrkdwn', text: 'Sales-only mode preserves dials/talk/leads. Catches sales entered after the daily scrape ran.' }] }\n ]\n};\nreturn [{ json: message }];"
},
"id": "ls-summary",
"name": "Build Slack Summary",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
240,
720
]
},
{
"parameters": {
"method": "POST",
"url": "YOUR_SLACK_INBOUND_WEBHOOK_URL",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json) }}",
"options": {
"timeout": 10000
}
},
"id": "ls-slack",
"name": "Send to Slack",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
480,
720
]
}
],
"connections": {
"Schedule (7am, 1pm, 8pm CST)": {
"main": [
[
{
"node": "Compute Backfill Dates",
"type": "main",
"index": 0
}
]
]
},
"Compute Backfill Dates": {
"main": [
[
{
"node": "Start Apify Backfill",
"type": "main",
"index": 0
}
]
]
},
"Start Apify Backfill": {
"main": [
[
{
"node": "Wait 90s",
"type": "main",
"index": 0
}
]
]
},
"Wait 90s": {
"main": [
[
{
"node": "Poll Run Status",
"type": "main",
"index": 0
}
]
]
},
"Poll Run Status": {
"main": [
[
{
"node": "Run Done?",
"type": "main",
"index": 0
}
]
]
},
"Run Done?": {
"main": [
[
{
"node": "Fetch Dataset",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait & Retry",
"type": "main",
"index": 0
}
]
]
},
"Wait & Retry": {
"main": [
[
{
"node": "Poll Run Status",
"type": "main",
"index": 0
}
]
]
},
"Fetch Dataset": {
"main": [
[
{
"node": "Data OK?",
"type": "main",
"index": 0
}
]
]
},
"Data OK?": {
"main": [
[
{
"node": "Split Per Date",
"type": "main",
"index": 0
}
],
[]
]
},
"Split Per Date": {
"main": [
[
{
"node": "Ingest sales_only",
"type": "main",
"index": 0
}
]
]
},
"Ingest sales_only": {
"main": [
[
{
"node": "Aggregate Results",
"type": "main",
"index": 0
}
]
]
},
"Aggregate Results": {
"main": [
[
{
"node": "Build Slack Summary",
"type": "main",
"index": 0
}
]
]
},
"Build Slack Summary": {
"main": [
[
{
"node": "Send to Slack",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"meta": {
"templateCredsSetupCompleted": true
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
DSB Sales Late Sweep. Uses httpRequest. Scheduled trigger; 14 nodes.
Source: https://github.com/eyecrackcodes/ROLI/blob/c81fc3ed47dbffc5f626193066a2443f9c3d19a8/n8n/dsb-sales-late-sweep.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