AutomationFlowsAI & RAG › Async Standup Bot

Async Standup Bot

15 - Async Standup Bot. Uses googleSheets, slack, anthropic. Scheduled trigger; 10 nodes.

Cron / scheduled trigger★★★★☆ complexityAI-powered10 nodesGoogle SheetsSlackAnthropic
AI & RAG Trigger: Cron / scheduled Nodes: 10 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #skynetlabs-15 — we link there as the canonical source.

This workflow follows the Google Sheets → Slack recipe pattern — see all workflows that pair these two integrations.

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 →

Download .json
{
  "name": "15 - Async Standup Bot",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "30 9 * * 1-5"
            }
          ]
        }
      },
      "id": "trigger-cron",
      "name": "Weekdays 9:30",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        200,
        300
      ]
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "REPLACE_ME_ROSTER_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "roster",
          "mode": "name"
        },
        "options": {}
      },
      "id": "read-roster",
      "name": "Read team roster",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        420,
        300
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const rows = $input.all().map(i => i.json).filter(r => String(r.active).toLowerCase() === 'true' || r.active === true);\nreturn rows.map(r => ({ json: r }));"
      },
      "id": "filter-active",
      "name": "Filter active members",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        640,
        300
      ]
    },
    {
      "parameters": {
        "select": "user",
        "user": {
          "__rl": true,
          "value": "={{ $json.slack_user_id }}",
          "mode": "id"
        },
        "text": "=Morning {{ $json.name }} :sun_with_face:\n\nQuick async standup. Reply in this DM, or use /standup-skip if OOO.\n\n1) What did you ship yesterday?\n2) What's the plan today?\n3) Anything blocking you?\n\nI'll roll everyone's answers into #standup at 10:15.",
        "otherOptions": {}
      },
      "id": "slack-dm",
      "name": "DM each member",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        860,
        300
      ],
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "amount": 45,
        "unit": "minutes"
      },
      "id": "wait-45",
      "name": "Wait 45 min",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1080,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const since = Math.floor((Date.now() - 60 * 60 * 1000) / 1000);\nreturn [{ json: { oldest: since } }];"
      },
      "id": "build-window",
      "name": "Build time window",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1300,
        300
      ]
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "getAll",
        "channel": {
          "__rl": true,
          "value": "={{ $('Filter active members').first().json.slack_user_id }}",
          "mode": "id"
        },
        "returnAll": false,
        "limit": 30,
        "filters": {
          "oldest": "={{ $json.oldest }}"
        }
      },
      "id": "read-dms",
      "name": "Read each DM thread",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        1520,
        300
      ],
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "continueOnFail": true
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "claude-sonnet-4-6",
          "mode": "list"
        },
        "messages": {
          "values": [
            {
              "content": "=You summarise async standup replies into one clean Slack post. Use Slack mrkdwn.\n\nTeam replies (mixed across people):\n{{ JSON.stringify($json) }}\n\nReturn ONLY valid JSON:\n{\n  \"summary_md\": \"the full Slack post body\",\n  \"blockers\": [\"name: blocker text\", \"...\"],\n  \"missing\": [\"names that did not reply\"]\n}\n\nFormat for summary_md:\n*:wave: Async standup \u2014 {{ $now.format('EEE dd MMM') }}*\n\nThen one block per person:\n*Name*\n\u2022 Yesterday: ...\n\u2022 Today: ...\n\u2022 Blockers: ... (or 'none')\n\nEnd with a :rotating_light: line if anyone is blocked, tagging the team lead user ID <@U_LEAD>.",
              "role": "user"
            }
          ]
        },
        "options": {
          "temperature": 0.3,
          "maxTokens": 2000
        }
      },
      "id": "claude-summary",
      "name": "Claude summary",
      "type": "@n8n/n8n-nodes-langchain.anthropic",
      "typeVersion": 1.2,
      "position": [
        1740,
        300
      ],
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const raw = $input.first().json.content?.[0]?.text || $input.first().json.text || '';\nconst match = raw.match(/\\{[\\s\\S]*\\}/);\nif (!match) throw new Error('No JSON');\nreturn [{ json: JSON.parse(match[0]) }];"
      },
      "id": "parse-summary",
      "name": "Parse summary JSON",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1960,
        300
      ]
    },
    {
      "parameters": {
        "channel": "REPLACE_ME_STANDUP_CHANNEL_ID",
        "text": "={{ $json.summary_md }}",
        "otherOptions": {}
      },
      "id": "slack-post-recap",
      "name": "Post recap to #standup",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [
        2180,
        300
      ],
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Weekdays 9:30": {
      "main": [
        [
          {
            "node": "Read team roster",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read team roster": {
      "main": [
        [
          {
            "node": "Filter active members",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter active members": {
      "main": [
        [
          {
            "node": "DM each member",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DM each member": {
      "main": [
        [
          {
            "node": "Wait 45 min",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 45 min": {
      "main": [
        [
          {
            "node": "Build time window",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build time window": {
      "main": [
        [
          {
            "node": "Read each DM thread",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read each DM thread": {
      "main": [
        [
          {
            "node": "Claude summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude summary": {
      "main": [
        [
          {
            "node": "Parse summary JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse summary JSON": {
      "main": [
        [
          {
            "node": "Post recap to #standup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateId": "skynetlabs-15"
  },
  "tags": [
    {
      "name": "skynetlabs-pack"
    },
    {
      "name": "standup"
    },
    {
      "name": "slack"
    }
  ]
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

15 - Async Standup Bot. Uses googleSheets, slack, anthropic. Scheduled trigger; 10 nodes.

Source: https://github.com/waseemnasir2k26/skynet-automation-pack/blob/main/n8n/15-async-standup-bot.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

06 - AEO Citation Monitor (4 LLMs). Uses googleSheets, anthropic, httpRequest, slack. Scheduled trigger; 11 nodes.

Google Sheets, Anthropic, HTTP Request +1
AI & RAG

01 - LinkedIn DM ICP Scorer. Uses httpRequest, anthropic, slack, googleSheets. Scheduled trigger; 9 nodes.

HTTP Request, Anthropic, Slack +1
AI & RAG

09 - GSC anomaly bot. Uses httpRequest, anthropic, slack, googleSheets. Scheduled trigger; 8 nodes.

HTTP Request, Anthropic, Slack +1
AI & RAG

03 - Cold Email Warm-up + Reply Detector. Uses emailReadImap, anthropic, slack, googleSheets. Scheduled trigger; 7 nodes.

Email Read Imap, Anthropic, Slack +1
AI & RAG

Imagine a dedicated financial expert tirelessly working behind the scenes, sifting through every transaction, every investment move, and every accounting entry. That's exactly what this automated syst

HTTP Request, Google Sheets, OpenAI +3