{
  "id": "HOUgh8szaduAUM7v",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Interview Scheduler with AI Reminders & Slack Approval",
  "tags": [],
  "nodes": [
    {
      "id": "e1e600af-68bb-40ee-9ea9-4892d3e0082e",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        0,
        48
      ],
      "parameters": {
        "path": "interview-calendar-meet",
        "options": {}
      },
      "typeVersion": 2.1
    },
    {
      "id": "beaa4d9f-6ed1-4a4c-9e05-a3d2dcbb3be0",
      "name": "Formatting the start date/time",
      "type": "n8n-nodes-base.set",
      "position": [
        208,
        48
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "37125224-1ca0-4812-abb3-0842d0afc70b",
              "name": "when",
              "type": "string",
              "value": "={{ $json.when.marked_at }}T{{ $json.when.start_at }}:00.000-05:00"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "89edb4de-bddd-44a2-bdaf-4f80688d6d41",
      "name": "Create the interview and invites the candidate",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        432,
        48
      ],
      "parameters": {
        "end": "={{ $json.when }}",
        "start": "={{ $json.when }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": ""
        },
        "additionalFields": {
          "summary": "=Interview for {{ $json.vacancy }}",
          "attendees": [
            "={{ $json.candidate.email }}, {{ $json.participants[0].email }}"
          ],
          "conferenceDataUi": {
            "conferenceDataValues": {
              "conferenceSolution": "hangoutsMeet"
            }
          }
        }
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "2bd551be-0803-4913-9a12-434102de247c",
      "name": "Creates the e-mail content",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        640,
        48
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.o-mini",
          "cachedResultName": "GPT-4.O-MINI"
        },
        "options": {},
        "responses": {
          "values": [
            {
              "content": "=Interview information:\n- Start time: {{ $json.start.dateTime }}\n- Name: {{ $json.summary }}\n- Interview meet URL: {{ $json.conferenceData.entryPoints[0].uri }}\n\nCandidate Information:\n- Name: {{ $('Formatting the start date/time').item.json.candidate.name }}"
            },
            {
              "role": "system",
              "content": "You are going to receive information about a video conference about a job interview from Recrutei ATS that was dated in google calendar and created a meet link.\n\nYour job is to, with all that information, create a message to remind the candidate about the interview, the e-mail will be sent 1 day before the actual day of the conference\n\nThe e-mail must be in EN-US, with a professional tone.\n\nOnly output the content of the e-mail, nothing more. Do not output a subject, only the content, without title, only the text necessary\n\nDo not sign the message."
            }
          ]
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "4f8691e4-ba78-4f6c-bdbc-5546ad0e813e",
      "name": "Separates the text",
      "type": "n8n-nodes-base.set",
      "position": [
        992,
        48
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "61ce13b2-9a7e-45d7-984e-b760ded183f9",
              "name": "text",
              "type": "string",
              "value": "={{ $json.output[0].content[0].text }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "eb9768b8-dac9-4edc-b7d6-c057aa34dcde",
      "name": "Wait until 1 day before the interview",
      "type": "n8n-nodes-base.wait",
      "notes": "You can personalize as you need",
      "position": [
        1200,
        48
      ],
      "parameters": {
        "resume": "specificTime",
        "dateTime": "={{ DateTime.fromISO($('Create the interview and invites the candidate').item.json.start.dateTime).minus({ days: 1 }).toISO() }}"
      },
      "notesInFlow": true,
      "typeVersion": 1.1
    },
    {
      "id": "882540c2-3fb0-4f16-b970-af72331e8b77",
      "name": "Send the message to the candidate",
      "type": "n8n-nodes-base.gmail",
      "notes": "The source node of the data is specified to prevent errors.",
      "position": [
        1408,
        48
      ],
      "parameters": {
        "sendTo": "={{ $('Formatting the start date/time').item.json.candidate.email }}",
        "message": "={{ $('Separates the text').item.json.text }}",
        "options": {
          "senderName": "={{ $('Formatting the start date/time').item.json.participants[0].name }}",
          "appendAttribution": false
        },
        "subject": "={{ $('Create the interview and invites the candidate').item.json.summary }}",
        "emailType": "text"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 2.1
    },
    {
      "id": "fe72fa9e-ef23-4361-88fc-af7b55611e66",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        -512
      ],
      "parameters": {
        "width": 1008,
        "height": 400,
        "content": "## Overview: AI-Powered Interview Scheduling & Reminders\n\nThis workflow automates the end-to-end process of scheduling technical or behavioral interviews. It captures interview data via Webhook, creates a Google Calendar event with an integrated Google Meet link, generates a personalized, professional reminder email using AI (GPT-4.1-mini), and automatically sends it via Gmail exactly 24 hours before the interview.\n\n## Setup Instructions\n\nTo implement this workflow, follow these steps:\n\n1. **Webhook:** Copy the Production URL and configure it in your Recrutei ATS to send interview JSON data.\n2. **Google Calendar:** Connect your account. The node is pre-configured to generate a \"hangoutsMeet\" conference link.\n3. **OpenAI:** Requires a valid API Key. The AI acts as a Recruitment Assistant to draft a professional email body.\n4. **Wait Node:** Currently set to \"1 day before\" the interview. You can adjust this timing based on your communication policy.\n5. **Gmail:** Connect your recruiter or company account to perform the final delivery.\n6. **Recrutei's API:** Inserts your Recrutei token in the Authorization header"
      },
      "typeVersion": 1
    },
    {
      "id": "c45da63d-2c4a-48b7-b534-c4236af87ae6",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 624,
        "height": 352,
        "content": "## Ingestion & Scheduling\nThe workflow triggers when candidate and vacancy details are received. A specialized Code node formats the date/time into ISO strings, ensuring Google Calendar creates the event at the correct time and generates the required video conference URL."
      },
      "typeVersion": 1
    },
    {
      "id": "f03f7424-6d46-4b58-97f9-d249cda66b72",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        592,
        -96
      ],
      "parameters": {
        "color": 6,
        "width": 544,
        "height": 352,
        "content": "## AI-Driven Copywriting\nInstead of using rigid templates, the OpenAI node receives specific meeting details (candidate name, vacancy, and Meet link). It drafts a professional, humanized reminder email in English, tailoring the tone for a high-quality candidate experience."
      },
      "typeVersion": 1
    },
    {
      "id": "c0186b6e-d592-4cc8-8121-f9ad1a418816",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1152,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 624,
        "height": 352,
        "content": "## Smart Delivery\nTo maximize attendance, the workflow enters a \"Wait\" state. It calculates the optimal time to send the reminder (24 hours before the session) and then automatically triggers the Gmail node to send the AI-generated content. Then, it register in the observations on the ATS."
      },
      "typeVersion": 1
    },
    {
      "id": "16e3fd8f-b31f-4ee9-a574-1d63e06974bb",
      "name": "Adding observation in candidate",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1616,
        48
      ],
      "parameters": {
        "url": "https://api.recrutei.com.br/api/v2/talents-observations",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "application_id",
              "value": "={{ $('Webhook').item.json.application_id }}"
            },
            {
              "name": "talent_id",
              "value": "={{ $('Webhook').item.json.id }}"
            },
            {
              "name": "talent_observation_type_id",
              "value": "7"
            },
            {
              "name": "description",
              "value": "=Candidate interview already set on Google Meet:\n{{ $('Create the interview and invites the candidate').item.json.conferenceData.entryPoints[0].uri }}"
            },
            {
              "name": "action",
              "value": "1"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer [your_token]"
            },
            {
              "name": "Content-Type",
              "value": "multipart/form-data"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "87949356-4ec5-4ba1-8789-a538aedf8030",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        1824,
        48
      ],
      "parameters": {
        "resume": "specificTime",
        "dateTime": "={{ $('Formatting the start date/time').item.json.when }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "56924ef0-840f-4525-a925-f7a2316ffa5c",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        2240,
        48
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c6a61bfa-42a8-44c5-bb68-1f536c82d182",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.data.approved }}",
              "rightValue": ""
            }
          ]
        }
      },
      "executeOnce": true,
      "typeVersion": 2.2
    },
    {
      "id": "abbc00db-760d-4b51-97df-14d786c66f0b",
      "name": "Adding observation in candidate1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2544,
        -176
      ],
      "parameters": {
        "url": "https://api.recrutei.com.br/api/v2/talents-observations",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "application_id",
              "value": "={{ $('Webhook').item.json.application_id }}"
            },
            {
              "name": "talent_id",
              "value": "={{ $('Webhook').item.json.id }}"
            },
            {
              "name": "talent_observation_type_id",
              "value": "7"
            },
            {
              "name": "description",
              "value": "=Candidate was approved to proceed to the next fase!"
            },
            {
              "name": "action",
              "value": "1"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer [your_token]"
            },
            {
              "name": "Content-Type",
              "value": "multipart/form-data"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "542400b1-7e99-48b8-9535-8c0158d4288b",
      "name": "Getting the vacancy",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2752,
        -176
      ],
      "parameters": {
        "url": "=https://api.recrutei.com.br/api/v1/vacancies/{{ $('Adding observation in candidate').item.json.data.vacancy.id }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer [your_token]"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "847b3961-920d-4778-b3c5-4009352f6365",
      "name": "Selecting pipe stage id",
      "type": "n8n-nodes-base.set",
      "notes": "You must personalize this as you need",
      "position": [
        2960,
        -176
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "86700f84-3364-4d9c-a07d-bce7e5935c88",
              "name": "last_stage",
              "type": "number",
              "value": "={{ $json.pipe_vacancy.pipe.pipe_stages[7].id }}"
            }
          ]
        }
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "aa50dec9-d9f9-442b-9944-799b56d1f7ab",
      "name": "Moving candidate to last stage",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3168,
        -176
      ],
      "parameters": {
        "url": "=https://api.recrutei.com.br/api/v1/applications/{{ $('Webhook').item.json.application_id }}/pipe",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "pipe_stage_id",
              "value": "={{ $json.last_stage }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer [your_token]"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "d7034174-e608-4e51-bdc9-d0242c648cda",
      "name": "Reproval e-mail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2544,
        192
      ],
      "parameters": {
        "sendTo": "={{ $('Webhook').item.json.candidate.email }}",
        "message": "=Dear {{ $('Webhook').item.json.candidate.name }},\n\nThank you very much for your interest in the {{ $('Webhook').item.json.vacancy }} position and for taking the time to participate in our selection process.\n\nAfter careful consideration, we regret to inform you that we will not be moving forward with your application at this time. Although your profile shows strong qualities, we have decided to proceed with candidates whose experience more closely matches our current needs for this role.\n\nWe wish you every success in your professional journey and thank you again for your interest in our company.\n\nKind regards",
        "options": {
          "appendAttribution": false
        },
        "subject": "={{ $('Webhook').item.json.vacancy }}",
        "emailType": "text"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 2.1
    },
    {
      "id": "714fa0c8-3a49-4134-bde9-ca337a4f914c",
      "name": "Request for candidate aproval",
      "type": "n8n-nodes-base.slack",
      "position": [
        2032,
        48
      ],
      "parameters": {
        "select": "channel",
        "message": "=The candidate {{ $('Webhook').item.json.candidate.name }} was approved and can proceed to the next fase?",
        "options": {
          "limitWaitTime": {
            "values": {
              "resumeAmount": 4
            }
          }
        },
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": ""
        },
        "operation": "sendAndWait",
        "approvalOptions": {
          "values": {
            "approvalType": "double"
          }
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "9a8f9ee3-3a93-4e49-af05-f119fff24c1c",
      "name": "Reproving candidate",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2752,
        192
      ],
      "parameters": {
        "url": "=https://api.recrutei.com.br/api/v1/applications/rate",
        "method": "PUT",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "application_id",
              "value": "={{ $('Webhook').item.json.application_id }}"
            },
            {
              "name": "rated",
              "value": "0"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer [your_token]"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "f2aa7f0f-02d8-49e6-be41-d35f06708347",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1792,
        -96
      ],
      "parameters": {
        "color": 6,
        "width": 608,
        "height": 352,
        "content": "## Approving/Declining candidate\nAfter the interview, it sends a message of approval/decline in a chosen channel in Slack"
      },
      "typeVersion": 1
    },
    {
      "id": "6651d5e5-3334-4a2b-89e0-598cf7c8b531",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2416,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 944,
        "height": 320,
        "content": "## Moving the candidate to the last pipe stage\nUsing the Recrutei's API, it adds an observation in the candidate informing the decision, it gets the vacancy to separate the pipe_stage_id of the last pipe stage, and then moves the candidate to the last stage"
      },
      "typeVersion": 1
    },
    {
      "id": "3cfddf26-7d84-4533-80b2-569d71f16d7f",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2416,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 944,
        "height": 368,
        "content": "## Reproving candidate\nIt sends a reproval e-mail to the candidate and then creates an observation in the candidate at the ATS informing the decision"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "2747946c-f3d2-46ae-9b20-fc3bde9598e3",
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Adding observation in candidate1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Reproval e-mail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "Request for candidate aproval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Formatting the start date/time",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reproval e-mail": {
      "main": [
        [
          {
            "node": "Reproving candidate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Separates the text": {
      "main": [
        [
          {
            "node": "Wait until 1 day before the interview",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send the message to the candidate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Getting the vacancy": {
      "main": [
        [
          {
            "node": "Selecting pipe stage id",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Selecting pipe stage id": {
      "main": [
        [
          {
            "node": "Moving candidate to last stage",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Creates the e-mail content": {
      "main": [
        [
          {
            "node": "Separates the text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Request for candidate aproval": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Formatting the start date/time": {
      "main": [
        [
          {
            "node": "Create the interview and invites the candidate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Adding observation in candidate": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Adding observation in candidate1": {
      "main": [
        [
          {
            "node": "Getting the vacancy",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send the message to the candidate": {
      "main": [
        [
          {
            "node": "Adding observation in candidate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait until 1 day before the interview": {
      "main": [
        [
          {
            "node": "Send the message to the candidate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create the interview and invites the candidate": {
      "main": [
        [
          {
            "node": "Creates the e-mail content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}