AutomationFlowsEmail & Gmail › Staff Scheduling Automation (publish + Confirm)

Staff Scheduling Automation (publish + Confirm)

Staff Scheduling Automation (Publish + Confirm). Uses googleSheets, gmail, googleSheetsTrigger. Scheduled trigger; 10 nodes.

Cron / scheduled trigger★★★★☆ complexity10 nodesGoogle SheetsGmailGoogle Sheets Trigger
Email & Gmail Trigger: Cron / scheduled Nodes: 10 Complexity: ★★★★☆ Added:

This workflow follows the Gmail → Google Sheets 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": "Staff Scheduling Automation (Publish + Confirm)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtHour": 23
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        0,
        0
      ],
      "id": "ecadba47-fa95-4503-971c-9449b95ce22c",
      "name": "Sunday Auto-Publish"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1ccHh2acYFEDpmj6aEeovI5FsoRU1bFbQ9ZADLyrCHGc",
          "mode": "list",
          "cachedResultName": "Local biz autopilot ",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ccHh2acYFEDpmj6aEeovI5FsoRU1bFbQ9ZADLyrCHGc/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 447491474,
          "mode": "list",
          "cachedResultName": "Weekly Schedule",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ccHh2acYFEDpmj6aEeovI5FsoRU1bFbQ9ZADLyrCHGc/edit#gid=447491474"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        208,
        0
      ],
      "id": "4076d14c-0653-44b8-a4e7-c8abeab8ce37",
      "name": "Read Weekly Schedule",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "fieldsToSummarize": {
          "values": [
            {
              "aggregation": "concatenate",
              "field": "Shift ID"
            },
            {
              "aggregation": "concatenate",
              "field": "Date"
            },
            {
              "aggregation": "concatenate",
              "field": "Shift Time"
            },
            {
              "aggregation": "concatenate",
              "field": "Role"
            }
          ]
        },
        "fieldsToSplitBy": "Staff Email",
        "options": {}
      },
      "type": "n8n-nodes-base.summarize",
      "typeVersion": 1.1,
      "position": [
        416,
        0
      ],
      "id": "1df34905-5947-4187-89cc-678807029fad",
      "name": "Summarize"
    },
    {
      "parameters": {
        "jsCode": "const results = [];\n\nfor (const item of $input.all()) {\n  const email = item.json['Staff_Email'];\n  const shiftIds = item.json['concatenated_Shift_ID'].split(',');\n  const dates = item.json['concatenated_Date'].split(',');\n  const times = item.json['concatenated_Shift_Time'].split(',');\n  const roles = item.json['concatenated_Role'].split(',');\n\n  let tableRows = '';\n  for (let i = 0; i < shiftIds.length; i++) {\n    const dateObj = new Date(dates[i]);\n    const formattedDate = isNaN(dateObj)\n      ? dates[i]\n      : dateObj.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' });\n\n    tableRows += `\n      <tr>\n        <td style=\"padding:8px 12px;border:1px solid #ddd;\">${formattedDate}</td>\n        <td style=\"padding:8px 12px;border:1px solid #ddd;\">${times[i]}</td>\n        <td style=\"padding:8px 12px;border:1px solid #ddd;\">${roles[i]}</td>\n        <td style=\"padding:8px 12px;border:1px solid #ddd;color:#888;\">${shiftIds[i]}</td>\n      </tr>`;\n  }\n\n  const scheduleTableHtml = `\n    <table style=\"border-collapse:collapse;font-family:Arial,sans-serif;font-size:14px;width:100%;max-width:500px;\">\n      <thead>\n        <tr style=\"background-color:#1F2937;color:#ffffff;\">\n          <th style=\"padding:8px 12px;border:1px solid #ddd;text-align:left;\">Date</th>\n          <th style=\"padding:8px 12px;border:1px solid #ddd;text-align:left;\">Time</th>\n          <th style=\"padding:8px 12px;border:1px solid #ddd;text-align:left;\">Role</th>\n          <th style=\"padding:8px 12px;border:1px solid #ddd;text-align:left;\">Shift ID</th>\n        </tr>\n      </thead>\n      <tbody>${tableRows}\n      </tbody>\n    </table>`;\n\n  results.push({\n    json: {\n      staffEmail: email,\n      scheduleTableHtml: scheduleTableHtml\n    }\n  });\n}\n\nreturn results;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        624,
        0
      ],
      "id": "f646d90f-fa2e-4f2e-9748-5875e9aa0c32",
      "name": "Format Schedule Email"
    },
    {
      "parameters": {
        "sendTo": "={{ $json.staffEmail }}",
        "subject": "Your Schedule for This Week",
        "message": "=Hi,<br><br>\nHere's your schedule for this week:<br><br>\n{{ $json.scheduleTableHtml }}\n<br><br>\nPlease confirm your shifts by replying to this email or using the confirmation link (coming soon).<br><br>\nThanks,<br>\n[Restaurant Name] Management",
        "options": {}
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        832,
        0
      ],
      "id": "653150c6-f69f-479f-a88a-06c33153e514",
      "name": "Send Schedule Email",
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const results = [];\n\nfor (const item of $input.all()) {\n  const shiftIds = item.json.staffEmail \n    ? $('Format Schedule Email').all().find(i => i.json.staffEmail === item.json.staffEmail)\n    : null;\n}\n\n// Simpler approach: pull directly from the Format Schedule Email node's original data\nconst formatted = $('Summarize').all();\n\nfor (const item of formatted) {\n  const ids = item.json['concatenated_Shift_ID'].split(',');\n  for (const id of ids) {\n    results.push({ json: { shiftId: id.trim() } });\n  }\n}\n\nreturn results;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1040,
        0
      ],
      "id": "1c57ec4b-1fcb-4a74-b285-0362a1ab7d83",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "1ccHh2acYFEDpmj6aEeovI5FsoRU1bFbQ9ZADLyrCHGc",
          "mode": "list",
          "cachedResultName": "Local biz autopilot ",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ccHh2acYFEDpmj6aEeovI5FsoRU1bFbQ9ZADLyrCHGc/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 447491474,
          "mode": "list",
          "cachedResultName": "Weekly Schedule",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ccHh2acYFEDpmj6aEeovI5FsoRU1bFbQ9ZADLyrCHGc/edit#gid=447491474"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Shift ID": "={{ $json.shiftId }}",
            "Notified?": "TRUE"
          },
          "matchingColumns": [
            "Shift ID"
          ],
          "schema": [
            {
              "id": "Shift ID",
              "displayName": "Shift ID",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Date",
              "displayName": "Date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Shift Time",
              "displayName": "Shift Time",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Role",
              "displayName": "Role",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Staff Assigned",
              "displayName": "Staff Assigned",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Staff Email",
              "displayName": "Staff Email",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Notified?",
              "displayName": "Notified?",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Confirmed?",
              "displayName": "Confirmed?",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Needs Coverage?",
              "displayName": "Needs Coverage?",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Covered By",
              "displayName": "Covered By",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "row_number",
              "displayName": "row_number",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "readOnly": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1248,
        0
      ],
      "id": "0981e2b7-0173-4ce8-979a-0f6ae8514a3a",
      "name": "Update row in sheet",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "1ccHh2acYFEDpmj6aEeovI5FsoRU1bFbQ9ZADLyrCHGc",
          "mode": "list",
          "cachedResultName": "Local biz autopilot ",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ccHh2acYFEDpmj6aEeovI5FsoRU1bFbQ9ZADLyrCHGc/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 447491474,
          "mode": "list",
          "cachedResultName": "Weekly Schedule",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ccHh2acYFEDpmj6aEeovI5FsoRU1bFbQ9ZADLyrCHGc/edit#gid=447491474"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Shift ID": "={{ $json['Which Shift ID are you confirming? '] }}",
            "Confirmed?": "TRUE"
          },
          "matchingColumns": [
            "Shift ID"
          ],
          "schema": [
            {
              "id": "Shift ID",
              "displayName": "Shift ID",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "Date",
              "displayName": "Date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Shift Time",
              "displayName": "Shift Time",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Role",
              "displayName": "Role",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Staff Assigned",
              "displayName": "Staff Assigned",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Staff Email",
              "displayName": "Staff Email",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Notified?",
              "displayName": "Notified?",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Confirmed?",
              "displayName": "Confirmed?",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Needs Coverage?",
              "displayName": "Needs Coverage?",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Covered By",
              "displayName": "Covered By",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "row_number",
              "displayName": "row_number",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "readOnly": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        208,
        384
      ],
      "id": "44a60089-0a2d-4ef3-886a-2866f1fdcb6e",
      "name": "Mark Confirmed",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "documentId": {
          "__rl": true,
          "value": "1n39mnFOlAP5GgP58G70jcBrSCOo_Jx2IFv7ffrVEMTk",
          "mode": "list",
          "cachedResultName": "Confirmations",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n39mnFOlAP5GgP58G70jcBrSCOo_Jx2IFv7ffrVEMTk/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": 477781584,
          "mode": "list",
          "cachedResultName": "Form Responses 1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1n39mnFOlAP5GgP58G70jcBrSCOo_Jx2IFv7ffrVEMTk/edit#gid=477781584"
        },
        "event": "rowAdded",
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "typeVersion": 1,
      "position": [
        -16,
        384
      ],
      "id": "dfd32895-b2db-4976-b4b3-08050a92d989",
      "name": "New Confirmation Submitted",
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        192
      ],
      "id": "728d61d8-beec-499f-bb1a-d732c6820db3",
      "name": "Manager Force-Send"
    }
  ],
  "connections": {
    "Sunday Auto-Publish": {
      "main": [
        [
          {
            "node": "Read Weekly Schedule",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Weekly Schedule": {
      "main": [
        [
          {
            "node": "Summarize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Summarize": {
      "main": [
        [
          {
            "node": "Format Schedule Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Schedule Email": {
      "main": [
        [
          {
            "node": "Send Schedule Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Schedule Email": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Update row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New Confirmation Submitted": {
      "main": [
        [
          {
            "node": "Mark Confirmed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manager Force-Send": {
      "main": [
        [
          {
            "node": "Read Weekly Schedule",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "versionId": "b2394f1d-9410-4743-99c0-bcf7eb7c4e19",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodeGroups": [],
  "id": "jmfZ4YjyLyNx5rUo",
  "tags": []
}

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

Staff Scheduling Automation (Publish + Confirm). Uses googleSheets, gmail, googleSheetsTrigger. Scheduled trigger; 10 nodes.

Source: https://github.com/justinrhedgep-star/cafe-autopilot-n8n/blob/main/03-staff-scheduler.json — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

This n8n workflow automates scheduled checks for your target keywords’ positions in Google’s organic search results using SerpAPI. It logs new positions in Google Sheets, compares them to previous ran

HTTP Request, Google Sheets, Google Sheets Trigger +2
Email & Gmail

YOUR_ID 4. Uses gmail, googleDrive, googleSheets, httpRequest. Scheduled trigger; 53 nodes.

Gmail, Google Drive, Google Sheets +1
Email & Gmail

special-day-email-sender. Uses googleSheets, gmail. Scheduled trigger; 43 nodes.

Google Sheets, Gmail
Email & Gmail

Looking for a way to track GitHub bounty issues automatically and get notified in real time? This GitHub Bounty Tracker workflow monitors repositories for issues labeled 💎 Bounty, logs them in Google

Google Sheets, HTTP Request, WhatsApp +1
Email & Gmail

Automatically processes new orders added to Google Sheets. Small orders are approved instantly; large orders trigger an HTML email with one-click Approve / Reject links — each handled by an independen

Google Sheets Trigger, Google Sheets, Gmail +1