HR helpdesk AI agent on Telegram

Employees ask about leave, policies and payday on Telegram; the agent answers from your handbook and logs leave requests.

AI agentAdvanced Saves ~15 hours/month AI AgentsBy Flowngn team

Works with n8n · Make · Zapier

How it works

Trigger: Telegram — an employee messages the bot
  1. An employee asks a question in the HR bot.
  2. The agent searches your policy sheet and answers from it.
  3. For leave, it checks their balance, confirms the dates and logs the request.
  4. It alerts HR about new requests and anything it can't answer.

What it can do

Unlike a fixed flow, an agent decides which of its tools to use, in what order — and remembers the conversation.

  • Answers policy questions in Arabic or English from your handbook
  • Looks up an employee's leave balance by their Telegram ID
  • Logs leave requests with dates, type and reason
  • Passes complaints and sensitive topics straight to HR

System prompt

The instructions that shape the agent. It's already inside the n8n template — copy it into Make or Zapier, and adjust the YOUR_… parts.

System prompt
You are the HR assistant for YOUR_COMPANY on Telegram. Reply in the employee's language (Arabic or English). Be friendly, clear and brief. Today is {{ $now.toFormat('cccc d LLLL yyyy') }}.

The employee's Telegram ID is {{ $('Get message').item.json.userId }}.

Tools:
- "Search policies": our HR handbook (topic, policy). Use it for any question about leave, working hours, remote work, payday, benefits or expenses. Answer only from it.
- "My record": this employee's own record (name, department, manager, leave balances). It only ever returns the person who is asking.
- "Log leave request": logs a leave request. Use only after the employee confirms the type, start date, end date and reason, and after checking their balance.
- "Notify HR": alerts the HR team. Use it for every leave request, and for complaints, harassment, health, salary disputes, resignations, or anything you can't answer.

Rules:
- If "My record" returns nothing, tell the employee they're not registered yet and use "Notify HR".
- Never approve or reject leave. Tell them HR will confirm.
- Never share information about other employees, and never discuss anyone's salary.
- You don't give legal advice. For labour-law questions, say HR will follow up.
- For sensitive topics, don't ask for details: say a person from HR will contact them privately, and use "Notify HR".

Safety rules

Built into the prompt and the tools:

  • Only shows an employee their own record
  • Never approves leave itself; HR decides
  • Never gives legal advice or discusses other employees' salaries

Uses your own AI key (Claude, OpenAI or another model). Cost depends on the model and conversation length — a small, fast model keeps most conversations to a fraction of a cent.

Diagram

TIncoming messageTelegram Trigger{ }Get messageCodeHR agentAI AgentClaude modelAnthropic Chat Mo…◷MemorySimple MemoryGSearch policiesGoogle Sheets ToolGMy recordGoogle Sheets ToolGLog leave requ…Google Sheets ToolTNotify HRTelegram ToolTReply on TelegramTelegram
Trigger App Code AI Agent tool

Code

JavaScript used in the Code step — already included in the template below.

Get message · Code node
// Keeps only text messages and pulls out who sent it and what they asked.
const msg = $input.first().json.message;
if (!msg?.text) return [];

return [{
  json: {
    chatId: msg.chat.id,
    userId: msg.from.id,
    name: [msg.from.first_name, msg.from.last_name].filter(Boolean).join(" "),
    question: msg.text,
  },
}];

Build it

Pick your tool — same automation, step by step.

Copy and paste straight into an n8n canvas (Ctrl+V), or download and use Import from File. Then connect your own credentials and replace every YOUR_… placeholder. AI steps use a Claude model node — swap it for OpenAI, Gemini or Ollama if you prefer.

Workflow JSON
Show code (344 lines)
{
  "name": "HR helpdesk AI agent on Telegram (Flowngn)",
  "nodes": [
    {
      "parameters": {
        "content": "## HR helpdesk AI agent on Telegram\nEmployees ask about leave, policies and payday on Telegram; the agent answers from your handbook and logs leave requests.\n\n**Setup**\n1. Open each node and connect your own credentials.\n2. Replace every YOUR_… placeholder.\n3. Test once, then activate.\n\nFree template from Flowngn",
        "height": 260,
        "width": 340
      },
      "id": "3757cdac-8539-4016-a7ed-1a9451c69357",
      "name": "Setup notes",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -420,
        -60
      ]
    },
    {
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "id": "3757cdac-8539-4016-a7ed-1a9451c69357",
      "name": "Incoming message",
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1.1,
      "position": [
        0,
        0
      ],
      "webhookId": "3757cdac-8539-4016-a7ed-1a9451c69357"
    },
    {
      "parameters": {
        "jsCode": "// Keeps only text messages and pulls out who sent it and what they asked.\nconst msg = $input.first().json.message;\nif (!msg?.text) return [];\n\nreturn [{\n  json: {\n    chatId: msg.chat.id,\n    userId: msg.from.id,\n    name: [msg.from.first_name, msg.from.last_name].filter(Boolean).join(\" \"),\n    question: msg.text,\n  },\n}];"
      },
      "id": "3757cdac-8539-4016-a7ed-1a9451c69357",
      "name": "Get message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        300,
        0
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $('Get message').item.json.question }}",
        "options": {
          "systemMessage": "=You are the HR assistant for YOUR_COMPANY on Telegram. Reply in the employee's language (Arabic or English). Be friendly, clear and brief. Today is {{ $now.toFormat('cccc d LLLL yyyy') }}.\n\nThe employee's Telegram ID is {{ $('Get message').item.json.userId }}.\n\nTools:\n- \"Search policies\": our HR handbook (topic, policy). Use it for any question about leave, working hours, remote work, payday, benefits or expenses. Answer only from it.\n- \"My record\": this employee's own record (name, department, manager, leave balances). It only ever returns the person who is asking.\n- \"Log leave request\": logs a leave request. Use only after the employee confirms the type, start date, end date and reason, and after checking their balance.\n- \"Notify HR\": alerts the HR team. Use it for every leave request, and for complaints, harassment, health, salary disputes, resignations, or anything you can't answer.\n\nRules:\n- If \"My record\" returns nothing, tell the employee they're not registered yet and use \"Notify HR\".\n- Never approve or reject leave. Tell them HR will confirm.\n- Never share information about other employees, and never discuss anyone's salary.\n- You don't give legal advice. For labour-law questions, say HR will follow up.\n- For sensitive topics, don't ask for details: say a person from HR will contact them privately, and use \"Notify HR\"."
        }
      },
      "id": "3757cdac-8539-4016-a7ed-1a9451c69357",
      "name": "HR agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 1.9,
      "position": [
        600,
        0
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "claude-sonnet-5",
          "mode": "list"
        },
        "options": {}
      },
      "id": "3757cdac-8539-4016-a7ed-1a9451c69357",
      "name": "Claude model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        175,
        220
      ]
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $('Get message').item.json.chatId }}",
        "contextWindowLength": 10
      },
      "id": "3757cdac-8539-4016-a7ed-1a9451c69357",
      "name": "Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        345,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Returns our HR handbook: topic and policy text.",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Policies",
          "mode": "name"
        },
        "options": {}
      },
      "id": "3757cdac-8539-4016-a7ed-1a9451c69357",
      "name": "Search policies",
      "type": "n8n-nodes-base.googleSheetsTool",
      "typeVersion": 4.5,
      "position": [
        515,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Returns the record of the employee who is asking: name, department, manager, leave balances.",
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Employees",
          "mode": "name"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "telegram_id",
              "lookupValue": "={{ $('Get message').item.json.userId }}"
            }
          ]
        },
        "options": {}
      },
      "id": "3757cdac-8539-4016-a7ed-1a9451c69357",
      "name": "My record",
      "type": "n8n-nodes-base.googleSheetsTool",
      "typeVersion": 4.5,
      "position": [
        685,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Logs a confirmed leave request.",
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Leave requests",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "date": "={{ $now.toISO() }}",
            "telegram_id": "={{ $('Get message').item.json.userId }}",
            "name": "={{ $('Get message').item.json.name }}",
            "type": "={{ $fromAI('type', \"annual, sick, unpaid or other\", 'string') }}",
            "from": "={{ $fromAI('from', \"First day YYYY-MM-DD\", 'string') }}",
            "to": "={{ $fromAI('to', \"Last day YYYY-MM-DD\", 'string') }}",
            "reason": "={{ $fromAI('reason', \"Reason, or empty\", 'string') }}",
            "status": "pending"
          }
        },
        "options": {}
      },
      "id": "3757cdac-8539-4016-a7ed-1a9451c69357",
      "name": "Log leave request",
      "type": "n8n-nodes-base.googleSheetsTool",
      "typeVersion": 4.5,
      "position": [
        855,
        220
      ]
    },
    {
      "parameters": {
        "descriptionType": "manual",
        "toolDescription": "Alerts the HR team about leave requests, sensitive topics and questions you can't answer.",
        "chatId": "YOUR_HR_CHAT_ID",
        "text": "={{ $fromAI('message', \"What HR needs to know, including the employee's name\", 'string') }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "id": "3757cdac-8539-4016-a7ed-1a9451c69357",
      "name": "Notify HR",
      "type": "n8n-nodes-base.telegramTool",
      "typeVersion": 1.2,
      "position": [
        1025,
        220
      ]
    },
    {
      "parameters": {
        "chatId": "={{ $('Get message').item.json.chatId }}",
        "text": "={{ $json.output }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "id": "3757cdac-8539-4016-a7ed-1a9451c69357",
      "name": "Reply on Telegram",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        900,
        0
      ]
    }
  ],
  "connections": {
    "Incoming message": {
      "main": [
        [
          {
            "node": "Get message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get message": {
      "main": [
        [
          {
            "node": "HR agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HR agent": {
      "main": [
        [
          {
            "node": "Reply on Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude model": {
      "ai_languageModel": [
        [
          {
            "node": "HR agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Memory": {
      "ai_memory": [
        [
          {
            "node": "HR agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Search policies": {
      "ai_tool": [
        [
          {
            "node": "HR agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "My record": {
      "ai_tool": [
        [
          {
            "node": "HR agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Log leave request": {
      "ai_tool": [
        [
          {
            "node": "HR agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Notify HR": {
      "ai_tool": [
        [
          {
            "node": "HR agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}

Open in the playground to edit →

Tips

  • Add each employee's Telegram user ID to the Employees sheet so the agent knows who is asking.

Related flows