AutomationFlowsAI & RAG › Manage Pto Requests and Emoji Approvals with Slack, Gemini, and Google

Manage Pto Requests and Emoji Approvals with Slack, Gemini, and Google

Byiamvaar @iamvaar on n8n.io

This workflow captures Slack PTO requests and approval reactions, uses Google Gemini to extract date ranges and reasons, checks Google Calendar for conflicts, logs requests in Google Sheets, and on ✅ approval blocks the time in Google Calendar and notifies the requester.…

Event trigger★★★★☆ complexity25 nodesSlack TriggerHTTP RequestSlackGoogle SheetsGoogle Calendar
AI & RAG Trigger: Event Nodes: 25 Complexity: ★★★★☆ Added:

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

This workflow follows the Google Calendar → 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
{
  "id": "SfyMd7CZIGwozcKm",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "PTO Requests & Approvals with Emoji Reaction",
  "tags": [],
  "nodes": [
    {
      "id": "10a7cbd4-639a-461b-9c3c-601c764e34ca",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -768,
        -320
      ],
      "parameters": {
        "width": 480,
        "height": 944,
        "content": "## PTO Requests & Approvals with Emoji Reaction\n\n### How it works\n\n1. Triggers on Slack app mentions to initiate time-off requests or process approval reactions. \n2. Uses Gemini AI to extract requested dates from user messages.\n3. Automatically checks Google Calendar for scheduling conflicts.\n4. Logs pending requests or notifies users if conflicts are detected. \n5. Finalizes approvals by blocking calendar time and updating status in Google Sheets.\n\n### Setup steps\n\n- Connect your Slack bot with app mentions and reaction event permissions.\n- Configure your Google Calendar credentials and calendar IDs.\n- Set up a structured Google Sheet to manage request logs.\n- Input your Gemini API key to enable natural language processing.\n\n### Customization\n\nYou can modify the prompt for Gemini to change how specific leave types are categorized or adjust the Slack channel IDs for notifications.\n\n@[youtube](4gf33NgiVIQ)"
      },
      "typeVersion": 1
    },
    {
      "id": "34e3c6e1-00b8-4639-bf58-402e8a4725f7",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -224,
        -16
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 304,
        "content": "## Slack trigger and main router\n\nCaptures Slack app mentions and routes them based on event type."
      },
      "typeVersion": 1
    },
    {
      "id": "aa37d63f-64f2-4e99-8c0a-04c6f6ad2401",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        -208
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 272,
        "content": "## Extract request dates with Gemini\n\nConfigures parameters and extracts requested leave dates using Gemini."
      },
      "typeVersion": 1
    },
    {
      "id": "9aa88b87-7359-48f3-afe7-d2795be37c3a",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        912,
        -320
      ],
      "parameters": {
        "color": 7,
        "width": 864,
        "height": 496,
        "content": "## Check conflicts and log request\n\nChecks calendar conflicts and notifies the requester or logs the pending HR request."
      },
      "typeVersion": 1
    },
    {
      "id": "661fbe80-b4cb-40d4-8c9c-2c3795f20c02",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        256
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 320,
        "content": "## Fetch thread for approval\n\nFilters and fetches channel message threads for reaction-based approvals."
      },
      "typeVersion": 1
    },
    {
      "id": "fcffe4bf-ac8e-4871-aada-4eb3bb494ed2",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        672,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 304,
        "content": "## Validate approval conditions\n\nValidates approval conditions and configures admin parameters."
      },
      "typeVersion": 1
    },
    {
      "id": "eb2599ec-8890-4498-943c-0c32ba5ede91",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1136,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 864,
        "height": 304,
        "content": "## Approve, block time, and notify\n\nLooks up pending requests, blocks calendar time, updates sheets, and notifies the employee."
      },
      "typeVersion": 1
    },
    {
      "id": "61b10bf1-7c74-4a9f-aeee-2827831683eb",
      "name": "When App Mentioned",
      "type": "n8n-nodes-base.slackTrigger",
      "position": [
        -176,
        112
      ],
      "parameters": {
        "options": {},
        "trigger": [
          "app_mention",
          "reaction_added"
        ],
        "watchWorkspace": true
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "55b8a47e-413a-473f-b338-6436c7bd15f2",
      "name": "Prepare Request Config",
      "type": "n8n-nodes-base.set",
      "position": [
        288,
        -96
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "hr_channel_id",
              "type": "string",
              "value": "enter-your-hr-slack-channel"
            },
            {
              "id": "a2",
              "name": "team_calendar_id",
              "type": "string",
              "value": "enter-your-shared-calender-id-here"
            },
            {
              "id": "a5",
              "name": "gemini_model",
              "type": "string",
              "value": "gemini-3.1-flash-lite"
            },
            {
              "id": "a6",
              "name": "requester_id",
              "type": "string",
              "value": "={{ $json.user }}"
            },
            {
              "id": "a7",
              "name": "requester_text",
              "type": "string",
              "value": "={{ $json.blocks[0].elements[0].elements[1].text }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ad7a3028-6be9-4aff-8bec-75c20d0e4b4e",
      "name": "Extract Dates with Gemini",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        512,
        -96
      ],
      "parameters": {
        "url": "=https://generativelanguage.googleapis.com/v1beta/models/{{ $json.gemini_model }}:generateContent",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({\n  contents: [{\n    parts: [{\n      text: \"Today's date is \" + $now.toFormat('yyyy-MM-dd') + \". Extract the requested time-off date range and the reason for the time-off from this Slack message: \\\"\" + $json.requester_text + \"\\\". Resolve any relative or partial dates (e.g. month/day only) against today's date, assuming the nearest future occurrence. Respond with start_date, end_date, and reason, with dates in YYYY-MM-DD format.\"\n    }]\n  }],\n  generationConfig: {\n    responseMimeType: \"application/json\",\n    responseSchema: {\n      type: \"object\",\n      properties: {\n        start_date: { type: \"string\" },\n        end_date: { type: \"string\" },\n        reason: { type: \"string\", description: \"The extracted reason for the time-off\" }\n      },\n      required: [\"start_date\", \"end_date\", \"reason\"]\n    }\n  }\n}) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googlePalmApi"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d3fa28b6-008d-4659-8a92-98a890afe32f",
      "name": "Parse Date Output",
      "type": "n8n-nodes-base.set",
      "position": [
        736,
        -96
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "b1",
              "name": "start_date",
              "type": "string",
              "value": "={{ JSON.parse($json.candidates[0].content.parts[0].text).start_date }}"
            },
            {
              "id": "b2",
              "name": "end_date",
              "type": "string",
              "value": "={{ JSON.parse($json.candidates[0].content.parts[0].text).end_date }}"
            },
            {
              "id": "5f39768e-357e-440d-943e-5ae59de72872",
              "name": "=reason",
              "type": "string",
              "value": "={{ JSON.parse($json.candidates[0].content.parts[0].text).reason }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ade2b565-5d94-45e8-b205-72ba97209015",
      "name": "If Calendar Conflict",
      "type": "n8n-nodes-base.if",
      "position": [
        1184,
        -96
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c1",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.busy?.length || 0 }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "1c0792a2-7c22-4bb2-a8c9-9454dcae0acf",
      "name": "Warn Requester of Conflict",
      "type": "n8n-nodes-base.slack",
      "position": [
        1408,
        -208
      ],
      "parameters": {
        "text": "=Heads up \u2014 there are already events on the shared calendar for {{ $('Parse Date Output').item.json.start_date }} to {{ $('Parse Date Output').item.json.end_date }}. Reply to this message or ask HR directly if you'd still like to proceed with the request.",
        "user": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Prepare Request Config').item.json.requester_id }}"
        },
        "select": "user",
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "942afece-9ac9-4555-a822-9965720cbfaf",
      "name": "Post Request to HR Channel",
      "type": "n8n-nodes-base.slack",
      "position": [
        1408,
        0
      ],
      "parameters": {
        "text": "=PTO Request from <@{{ $('Prepare Request Config').item.json.requester_id }}> for {{ $('Parse Date Output').item.json.start_date }} to {{ $('Parse Date Output').item.json.end_date }}. \n\nReason: {{ $('Parse Date Output').item.json.reason }}\n\nReact with \u2705 to approve. {{ $('Parse Date Output').item.json['not specified'] }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Prepare Request Config').item.json.hr_channel_id }}"
        },
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "9b0fcd28-759b-47e0-b256-97e9f05fa62a",
      "name": "Log Pending Request in Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1632,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "End": "={{ $('Parse Date Output').item.json.end_date }}",
            "User": "={{ $('Prepare Request Config').item.json.requester_id }}",
            "Start": "={{ $('Parse Date Output').item.json.start_date }}",
            "reason": "={{ $('Parse Date Output').item.json.reason }}",
            "Message_ID": "=a{{ $json.message.ts }}a"
          },
          "schema": [
            {
              "id": "Message_ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Message_ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "User",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "User",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Start",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Start",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "End",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "End",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "reason",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "reason",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Approved_By",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Approved_By",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Message_ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/11z8NHpqXeTMoUBy_Yk_t3c76jND8F2OLSkvTqQYknck/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "11z8NHpqXeTMoUBy_Yk_t3c76jND8F2OLSkvTqQYknck",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/11z8NHpqXeTMoUBy_Yk_t3c76jND8F2OLSkvTqQYknck/edit?usp=drivesdk",
          "cachedResultName": "Leave Requests"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "ff281646-1f66-44e0-b2b6-e99dfccb8c4c",
      "name": "Check Reaction Criteria",
      "type": "n8n-nodes-base.if",
      "position": [
        960,
        400
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "e1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.reaction_name }}",
              "rightValue": "white_check_mark"
            },
            {
              "id": "e2",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.reacted_channel_id }}",
              "rightValue": "={{ $json.hr_channel_id }}"
            },
            {
              "id": "e3",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.reactor_id }}",
              "rightValue": "={{ $json.hr_admin_user_id }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ef9eea5e-dd70-420e-b4b3-417272dd1c98",
      "name": "Lookup Request in Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1184,
        384
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $json.message_ts }}",
              "lookupColumn": "Message_ID"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/11z8NHpqXeTMoUBy_Yk_t3c76jND8F2OLSkvTqQYknck/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "11z8NHpqXeTMoUBy_Yk_t3c76jND8F2OLSkvTqQYknck",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/11z8NHpqXeTMoUBy_Yk_t3c76jND8F2OLSkvTqQYknck/edit?usp=drivesdk",
          "cachedResultName": "Leave Requests"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "db96c0b5-5c4f-4c6f-8222-b31c6fa11a0d",
      "name": "Block Time in Calendar",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        1408,
        384
      ],
      "parameters": {
        "end": "={{ $json.End }}",
        "start": "={{ $json.Start }}",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "user@example.com",
          "__regex": "(^[a-zA-Z0-9.!#$%&\u2019*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)"
        },
        "additionalFields": {
          "allday": "yes",
          "summary": "=PTO - <@{{ $json.User }}> | "
        }
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "21aea902-8c61-4a70-8a9c-f3e78da03395",
      "name": "Prepare Reaction Config",
      "type": "n8n-nodes-base.set",
      "position": [
        736,
        400
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d1",
              "name": "hr_channel_id",
              "type": "string",
              "value": "enter-your-hr-slack-channel-id"
            },
            {
              "id": "d2",
              "name": "hr_admin_user_id",
              "type": "string",
              "value": "enter-your-hr-slack-user-id"
            },
            {
              "id": "d3",
              "name": "team_calendar_id",
              "type": "string",
              "value": "enter-your-team-calender-id-here"
            },
            {
              "id": "d6",
              "name": "reactor_id",
              "type": "string",
              "value": "={{ $('If Reaction Added').item.json.user }}"
            },
            {
              "id": "d7",
              "name": "reaction_name",
              "type": "string",
              "value": "={{ $('If Reaction Added').item.json.reaction }}"
            },
            {
              "id": "d8",
              "name": "reacted_channel_id",
              "type": "string",
              "value": "={{ $('If Reaction Added').item.json.item.channel }}"
            },
            {
              "id": "d9",
              "name": "message_ts",
              "type": "string",
              "value": "=a{{ $('If Reaction Added').item.json.item.ts }}a"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "95b093bb-d248-4aa0-a5b9-bdd4df9150cb",
      "name": "Check Calendar Conflicts",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        960,
        -96
      ],
      "parameters": {
        "options": {},
        "timeMax": "={{ $json.end_date }}T23:59:59",
        "timeMin": "={{ $json.start_date }}T00:00:00",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Prepare Request Config').item.json.team_calendar_id }}",
          "__regex": "(^[a-zA-Z0-9.!#$%&\u2019*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)"
        },
        "operation": "getAll",
        "returnAll": true
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3,
      "alwaysOutputData": true
    },
    {
      "id": "d29fac6d-7f5b-497e-a9de-25fbe6c02475",
      "name": "Route by Event Type",
      "type": "n8n-nodes-base.if",
      "position": [
        48,
        112
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "db7fd438-c324-4661-8718-44cc7ac5083d",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.type }}",
              "rightValue": "reaction_added"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "3481d500-c60e-4222-b488-0f3839f7c948",
      "name": "If Reaction Added",
      "type": "n8n-nodes-base.if",
      "position": [
        288,
        400
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "5d5c9cfb-5e0a-4feb-9093-4e64456b5422",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.type }}",
              "rightValue": "reaction_added"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "c1872c79-ff14-4bfd-8315-e0ad134ff50b",
      "name": "Notify Employee of Approval",
      "type": "n8n-nodes-base.slack",
      "position": [
        1856,
        384
      ],
      "parameters": {
        "text": "=Great news! Your PTO for {{ $('Lookup Request in Sheets').item.json.Start }} to {{ $('Lookup Request in Sheets').item.json.End }} has been approved and added to the calendar.",
        "user": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Lookup Request in Sheets').item.json.User }}"
        },
        "select": "user",
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "1226761e-92a8-43a6-bb4c-e006f67a8773",
      "name": "Fetch Thread Replies",
      "type": "n8n-nodes-base.slack",
      "position": [
        512,
        384
      ],
      "parameters": {
        "ts": "={{ $json.item.ts }}",
        "filters": {},
        "resource": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0BM0830P8F",
          "cachedResultName": "hr-requests"
        },
        "operation": "replies"
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "e085b1ba-31c8-4cda-88c8-685a901d9bf3",
      "name": "Update Status in Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1632,
        384
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "Approved",
            "Message_ID": "={{ $('Prepare Reaction Config').item.json.message_ts }}",
            "Approved_By": "={{ $('Prepare Reaction Config').item.json.hr_admin_user_id }}"
          },
          "schema": [
            {
              "id": "Message_ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Message_ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "User",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "User",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Start",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Start",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "End",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "End",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Approved_By",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Approved_By",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Message_ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/11z8NHpqXeTMoUBy_Yk_t3c76jND8F2OLSkvTqQYknck/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "11z8NHpqXeTMoUBy_Yk_t3c76jND8F2OLSkvTqQYknck",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/11z8NHpqXeTMoUBy_Yk_t3c76jND8F2OLSkvTqQYknck/edit?usp=drivesdk",
          "cachedResultName": "Leave Requests"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    }
  ],
  "active": true,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "b102c700-079d-447a-87ee-4d3c518bbef0",
  "nodeGroups": [],
  "connections": {
    "If Reaction Added": {
      "main": [
        [
          {
            "node": "Fetch Thread Replies",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Date Output": {
      "main": [
        [
          {
            "node": "Check Calendar Conflicts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When App Mentioned": {
      "main": [
        [
          {
            "node": "Route by Event Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Event Type": {
      "main": [
        [
          {
            "node": "Prepare Request Config",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "If Reaction Added",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Thread Replies": {
      "main": [
        [
          {
            "node": "Prepare Reaction Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Calendar Conflict": {
      "main": [
        [
          {
            "node": "Warn Requester of Conflict",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Post Request to HR Channel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Block Time in Calendar": {
      "main": [
        [
          {
            "node": "Update Status in Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Request Config": {
      "main": [
        [
          {
            "node": "Extract Dates with Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Reaction Criteria": {
      "main": [
        [
          {
            "node": "Lookup Request in Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Reaction Config": {
      "main": [
        [
          {
            "node": "Check Reaction Criteria",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Status in Sheets": {
      "main": [
        [
          {
            "node": "Notify Employee of Approval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Calendar Conflicts": {
      "main": [
        [
          {
            "node": "If Calendar Conflict",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lookup Request in Sheets": {
      "main": [
        [
          {
            "node": "Block Time in Calendar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Dates with Gemini": {
      "main": [
        [
          {
            "node": "Parse Date Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post Request to HR Channel": {
      "main": [
        [
          {
            "node": "Log Pending Request in Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

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

This workflow captures Slack PTO requests and approval reactions, uses Google Gemini to extract date ranges and reasons, checks Google Calendar for conflicts, logs requests in Google Sheets, and on ✅ approval blocks the time in Google Calendar and notifies the requester.…

Source: https://n8n.io/workflows/17772/ — 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

13195 Search Slack For N8N Templates With Openai Tips Google Sheets Cache And Weekly Analytics. Uses slackTrigger, googleSheets, httpRequest, slack. Event-driven trigger; 31 nodes.

Slack Trigger, Google Sheets, HTTP Request +2
AI & RAG

Monitor Google Drive folder, parsing PDF, DOCX and image file into a destination folder, ready for further processing (e.g. RAG ingestion, translation, etc.) Keep processing log in Google Sheet and se

Google Drive Trigger, Google Drive, HTTP Request +2
AI & RAG

This workflow monitors unread Gmail support emails, uses Google Gemini to classify and summarize each message and draft a reply, logs the ticket details to Google Sheets, creates a Gmail draft respons

Gmail Trigger, HTTP Request, Google Sheets +2
AI & RAG

Sales Lead Qualifier. Uses telegramTrigger, googleSheets, telegram, googleGemini. Event-driven trigger; 41 nodes.

Telegram Trigger, Google Sheets, Telegram +3
AI & RAG

Runs fetch_cat/linkedin-jobs-scraper daily to collect recent LinkedIn jobs, scores each listing against your candidate profile with OpenAI, saves qualified matches to Google Sheets, and sends the five

Data Table, HTTP Request, OpenAI +2