AutomationFlowsAI & RAG › Run a Telegram Voice Calendar Assistant with Google Calendar and Gmail

Run a Telegram Voice Calendar Assistant with Google Calendar and Gmail

ByAironauts AI @aironauts on n8n.io

Send a text or voice message to a Telegram bot and it books, reschedules, or cancels Google Calendar events for you. It resolves attendee names to email addresses, attaches a Meet link, and drafts a Gmail confirmation for external guests. A Telegram trigger receives every…

Event trigger★★★★☆ complexityAI-powered27 nodesGoogle Calendar ToolTelegram TriggerG Suite Admin ToolMemory Buffer WindowTelegramOpenAIGmail ToolAgent
AI & RAG Trigger: Event Nodes: 27 Complexity: ★★★★☆ AI nodes: yes Added:
Run a Telegram Voice Calendar Assistant with Google Calendar and Gmail — n8n workflow card showing Google Calendar Tool, Telegram Trigger, G Suite Admin Tool integration

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

This workflow follows the Agent → Agenttool 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": "xe8e6wBkklll8PeU",
  "meta": {
    "builderVariant": "mcp",
    "aiBuilderAssisted": true,
    "templateCredsSetupCompleted": true
  },
  "name": "Telegram voice and text calendar assistant with Google Calendar and Gmail drafts",
  "tags": [],
  "nodes": [
    {
      "id": "d426e493-283a-41ea-8604-23381d4957c9",
      "name": "Create Event",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        3760,
        2096
      ],
      "parameters": {
        "end": "={{ $fromAI('End', 'End datetime in ISO 8601 format with timezone, e.g. 2026-07-18T10:00:00+07:00', 'string') }}",
        "start": "={{ $fromAI('Start', 'Start datetime in ISO 8601 format with timezone, e.g. 2026-07-18T09:00:00+07:00', 'string') }}",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "primary"
        },
        "descriptionType": "manual",
        "toolDescription": "=Create a new event in Google Calendar with the first attendee. After creating, immediately call Update Calendar to add any remaining attendees one by one using the returned event ID. Attendee email addresses are provided already resolved by the calling agent, never attempt to look up a name yourself. If title, start time, or end time is missing, ask the calling agent for clarification instead of creating with incomplete data.",
        "additionalFields": {
          "summary": "={{ $fromAI('Title', 'Title of the event', 'string') }}",
          "location": "={{ $fromAI('Location', 'Physical address or meeting link', 'string') }}",
          "attendees": [
            "={{ $fromAI('Attendee_Email', 'Email address of the attendee', 'string') }}"
          ],
          "description": "={{ $fromAI('Description', 'Optional description or agenda for the event', 'string') }}",
          "sendUpdates": "all",
          "conferenceDataUi": {
            "conferenceDataValues": {
              "conferenceSolution": "hangoutsMeet"
            }
          }
        },
        "useDefaultReminders": "={{ $fromAI('Use_Default_Reminders', 'Whether to use default calendar reminders', 'boolean') }}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "5d3c0dc0-9d82-47fd-b551-c7d9a436dbfd",
      "name": "Get Events",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        4032,
        2096
      ],
      "parameters": {
        "options": {
          "orderBy": "startTime"
        },
        "timeMax": "={{ $fromAI('Time_To', 'End of time range in ISO 8601 with +07:00 offset. Defaults to 1 week from now if not specified.', 'string') }}",
        "timeMin": "={{ $fromAI('Time_From', 'Start of time range in ISO 8601 with +07:00 offset. Defaults to now if not specified.', 'string') }}",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "primary"
        },
        "operation": "getAll",
        "descriptionType": "manual",
        "toolDescription": "Retrieve events from Google Calendar within a time range. Use this before any delete or update action to confirm the correct event ID. Also use when the user asks what events they have on a specific day or period."
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "0680d242-b662-4302-aa59-fe2f7c7cc282",
      "name": "Update Calendar",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        3888,
        2096
      ],
      "parameters": {
        "eventId": "={{ $fromAI('Event_ID', 'ID of the event to update, returned by Create Event', 'string') }}",
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "primary"
        },
        "operation": "update",
        "updateFields": {
          "summary": "={{ $fromAI('Title', 'Updated event title, omit if not changing', 'string') }}",
          "attendeesUi": {
            "values": {
              "attendees": [
                "={{ $fromAI('Attendee_Email', 'Email address of the attendee to add', 'string') }}"
              ]
            }
          },
          "description": "={{ $fromAI('Description', 'Updated description, omit if not changing', 'string') }}"
        },
        "descriptionType": "manual",
        "toolDescription": "Update an existing Google Calendar event. Use this immediately after Create Event to add remaining attendees one by one using the event ID returned by Create Event. Can also update title, time, description, or location. Always pass sendUpdates as 'all' when adding attendees so invitees receive email notifications.",
        "useDefaultReminders": "={{ $fromAI('Use_Default_Reminders', 'Whether to use default calendar reminders', 'boolean') }}"
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "5274da5c-b67c-462e-9ab0-09fa8432e637",
      "name": "Extract Text Message",
      "type": "n8n-nodes-base.set",
      "position": [
        3536,
        1168
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "41b525e6-a59f-47c7-b6ee-2b88eaa386fe",
              "name": "text",
              "type": "string",
              "value": "={{ $json.message.text }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "d437e447-19a2-4783-ae8f-e09d362bce3f",
      "name": "Receive User Input (Telegram)",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        3088,
        1040
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "5e720190-cd1b-40fa-bf8a-4eede25d8f6c",
      "name": "Text or Voice?",
      "type": "n8n-nodes-base.if",
      "position": [
        3312,
        1040
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c64b00ea-20f1-4d44-aed6-1528d537fc84",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.message.voice.mime_type }}",
              "rightValue": "audio/ogg"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "aed1dc37-a2ba-4165-b7ed-4cfa96ad29ff",
      "name": "Delete Event",
      "type": "n8n-nodes-base.googleCalendarTool",
      "position": [
        4176,
        2096
      ],
      "parameters": {
        "eventId": "={{ $fromAI('Event_ID', 'ID of the event to delete. Must be retrieved via Get events and confirmed by the user.', 'string') }}",
        "options": {
          "sendUpdates": "all"
        },
        "calendar": {
          "__rl": true,
          "mode": "id",
          "value": "primary"
        },
        "operation": "delete",
        "descriptionType": "manual",
        "toolDescription": "Delete an event from Google Calendar. Always call Get Events first to confirm the correct event ID. Always ask the calling agent to confirm before deleting. Notify all attendees by setting sendUpdates to all."
      },
      "typeVersion": 1.3
    },
    {
      "id": "a0211283-5509-4273-ace5-b7cbf490b479",
      "name": "Get Contacts",
      "type": "n8n-nodes-base.gSuiteAdminTool",
      "position": [
        4304,
        1216
      ],
      "parameters": {
        "filter": {},
        "operation": "getAll",
        "descriptionType": "manual",
        "toolDescription": "Look up workspace members by name to retrieve their email address. Call this before adding any attendee who is referred to by name rather than email. Returns a list of users in the organization."
      },
      "typeVersion": 1
    },
    {
      "id": "42f6f417-8c81-4fdc-b31a-f581b795dc25",
      "name": "Orchestrator Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        4192,
        1376
      ],
      "parameters": {
        "sessionKey": "={{ $('Receive User Input (Telegram)').item.json.message.chat.id }}",
        "sessionIdType": "customKey",
        "contextWindowLength": 10
      },
      "typeVersion": 1.4
    },
    {
      "id": "2963b866-017c-4a2a-8333-e35eeb96989e",
      "name": "Send Response to Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        4544,
        1040
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "chatId": "={{ $('Receive User Input (Telegram)').item.json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6af7f0a9-91c9-4733-ad7a-f35575a64fff",
      "name": "Download Voice File",
      "type": "n8n-nodes-base.telegram",
      "position": [
        3536,
        928
      ],
      "parameters": {
        "fileId": "={{ $('Receive User Input (Telegram)').item.json.message.voice.file_id }}",
        "resource": "file",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2a350258-d1b7-49e7-a40c-de8dc8785d97",
      "name": "Transcribe Voice to Text",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        3744,
        928
      ],
      "parameters": {
        "options": {},
        "resource": "audio",
        "operation": "transcribe"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "2111bf47-c776-4030-aedb-3938a12f5b2f",
      "name": "Create Gmail Draft",
      "type": "n8n-nodes-base.gmailTool",
      "position": [
        4480,
        2048
      ],
      "parameters": {
        "message": "={{ $fromAI('Message', 'Email body, plain text or simple HTML', 'string') }}",
        "options": {
          "ccList": "={{ $fromAI('Cc_List', 'Optional CC email addresses, comma separated', 'string') }}",
          "sendTo": "={{ $fromAI('Send_To', 'Recipient email address', 'string') }}"
        },
        "subject": "={{ $fromAI('Subject', 'Email subject line', 'string') }}",
        "resource": "draft",
        "descriptionType": "manual",
        "toolDescription": "Create a draft email in Gmail. Never sends automatically. Use to draft meeting confirmation emails to attendees outside @yourcompany.com after a calendar event is created, or any other draft the user requests."
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "4b682292-e919-4cb7-baca-dec06e2d8bb6",
      "name": "Search Gmail Messages",
      "type": "n8n-nodes-base.gmailTool",
      "position": [
        4624,
        2048
      ],
      "parameters": {
        "limit": "={{ $fromAI('Max_Results', 'Maximum number of messages to return', 'number') }}",
        "simple": false,
        "filters": {
          "q": "={{ $fromAI('Query', 'Gmail search query, same syntax as Gmail search box', 'string') }}",
          "sender": "={{ $fromAI('Sender', 'Optional sender name or email to filter by', 'string') }}",
          "readStatus": "both"
        },
        "options": {},
        "operation": "getAll",
        "descriptionType": "manual",
        "toolDescription": "Search Gmail messages. Use to find context for drafting emails, such as prior threads with a specific attendee, or to answer user questions about recent emails."
      },
      "typeVersion": 2.2
    },
    {
      "id": "f3f76645-c0e4-4519-97cd-fbaa0bd345f3",
      "name": "Orchestrator Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        4032,
        1040
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "options": {
          "maxIterations": 30,
          "systemMessage": "=You are an assistant operating on behalf of the calendar owner, coordinating specialized sub-agents and resolving contact lookups directly.\n\nCurrent time: {{ $now.setZone('Asia/Ho_Chi_Minh').toISO() }}\nTimezone: Asia/Ho_Chi_Minh (UTC+7)\n\n## Tools\n- Get Contacts: resolve a person's name to their organization email address\n- Calendar Agent: handles all Google Calendar actions, create, update, delete, look up events. Expects attendee email addresses, not names.\n- Gmail Agent: drafts and searches Gmail, never sends email directly\n\n## Rules\n- When the user refers to an attendee by name rather than email, call Get Contacts yourself before delegating to Calendar Agent. Never pass a name to Calendar Agent, always resolve it first.\n- If an attendee email is outside the @yourcompany.com domain, ask the user to confirm the exact address before delegating.\n- When the user asks to create or update an event, delegate the full request to Calendar Agent, including all details you have (title, time, resolved attendee emails).\n- After Calendar Agent successfully creates or updates an event that includes at least one attendee outside the @yourcompany.com domain, call Gmail Agent to draft a confirmation email to that attendee. Pass along: recipient email, event title, start time, end time, and the Google Meet link if returned. Do this automatically, do not ask the user first, since it only creates a draft.\n- For delete requests, first delegate to Calendar Agent to look up and identify the matching event, without deleting yet. Present the event's title and time to the user and ask for explicit confirmation. Only after the user confirms, delegate again to Calendar Agent with an explicit instruction naming the confirmed event ID to delete.\n- If the user's request is ambiguous (missing date, time, recipient, or intent), ask the user directly, do not guess and do not delegate an incomplete request.\n- Respond in the same language the user writes in.\n- After an agent completes an action, summarize the result for the user in 1-2 sentences, do not repeat raw tool output.\n- Never confirm an action as completed unless the relevant sub-agent or tool reported a successful result.\n- Always respond in Telegram HTML format. Use only these tags: <b>, <i>, <code>, <pre>. Do not use markdown or any other formatting.",
          "returnIntermediateSteps": false
        },
        "promptType": "define"
      },
      "typeVersion": 1.9
    },
    {
      "id": "d7a451f9-23f9-4578-b2b8-d3a6fb885f16",
      "name": "Sub-agent Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        4400,
        2496
      ],
      "parameters": {
        "sessionKey": "={{ $('Receive User Input (Telegram)').item.json.message.chat.id }}",
        "sessionIdType": "customKey",
        "contextWindowLength": 10
      },
      "typeVersion": 1.4
    },
    {
      "id": "a1bf17b4-4916-4287-974e-9d948c922f5b",
      "name": "Sub-agent Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        4240,
        2496
      ],
      "parameters": {
        "model": "google/gemini-3.5-flash",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "52e1223c-2c94-4f98-8e69-c104adfba444",
      "name": "Calendar Agent",
      "type": "@n8n/n8n-nodes-langchain.agentTool",
      "position": [
        3920,
        1856
      ],
      "parameters": {
        "text": "={{ $fromAI('Task', 'Detailed instructions and all needed context for the calendar action, including intent (create, update, delete, or look up), dates, times, attendees, or event ID', 'string') }}",
        "options": {
          "maxIterations": 30,
          "systemMessage": "=You are a Google Calendar assistant operating on behalf of the calendar owner.\n\nCurrent time: {{ $now.setZone('Asia/Ho_Chi_Minh').toISO() }}\nTimezone: Asia/Ho_Chi_Minh (UTC+7)\n\n## Tools\n- Get Events: retrieve events within a time range\n- Create Event: create a new calendar event\n- Update Calendar: update an existing event\n- Delete Event: delete an event\n\n## Rules\n- Always derive the current date and time from the current time above before acting.\n- Attendee names are already resolved to email addresses by the calling agent before reaching you. If you receive a name instead of an email address, do not guess, report back to the calling agent that resolution is needed.\n- For delete requests: call Get Events to find the matching event and return its ID, title, and time to the calling agent without deleting. Only delete when the calling agent's request explicitly states the user has confirmed and names the specific event ID to delete.\n- For update actions: call Get Events first to confirm the event ID, then execute without additional confirmation.\n- If an attendee email is outside the organization domain, flag this back to the calling agent rather than guessing.\n- When creating an event with multiple attendees, add the first attendee via Create Event, then immediately call Update Calendar once per remaining attendee using the returned event ID. Do not wait for confirmation between these calls.\n- If title, date, time, or duration is ambiguous, ask for clarification rather than guessing.\n- All datetime values must be in ISO 8601 format with +07:00 offset.\n- Default event duration is 1 hour unless specified.\n- Never confirm an action as completed unless a tool was called and returned a successful response.\n- After completing an action, return a structured summary including event ID, title, start time, end time, and the Google Meet link if one was created."
        },
        "toolDescription": "Manages Google Calendar: create, update, delete, and look up events. Expects attendee email addresses already resolved, never pass a name. Provide the full request in natural language, including intent (create, update, delete, or look up), dates, times, and attendee emails. Returns event ID, title, start time, end time, and Google Meet link if applicable."
      },
      "typeVersion": 3
    },
    {
      "id": "25454d47-b4a0-4edb-a4fd-5963dae2671c",
      "name": "Gmail Agent",
      "type": "@n8n/n8n-nodes-langchain.agentTool",
      "position": [
        4448,
        1808
      ],
      "parameters": {
        "text": "={{ $fromAI('Task', 'Detailed instructions and all needed context, such as recipient, subject, event details, or search query, for the Gmail sub-agent to act on', 'string') }}",
        "options": {
          "maxIterations": 20,
          "systemMessage": "You are a Gmail assistant drafting and searching email on behalf of the calendar owner. Tools: Search Gmail Messages, Create Gmail Draft. When asked to draft a meeting confirmation, use the provided event details to write a clear, polite email in the language of the request. Subject format: Confirmed: <event title> - <date>. Never send email directly, only create drafts. If recipient or event details are missing, ask for clarification rather than guessing. After creating a draft, report back the draft ID and subject."
        },
        "toolDescription": "Drafts confirmation or follow-up emails and searches Gmail. When called after creating a calendar event with an external attendee, include: recipient email, event title, start time, end time, location or Google Meet link. This tool only creates drafts, it never sends email."
      },
      "typeVersion": 3
    },
    {
      "id": "5dbefea8-25f3-4f6c-8133-2a3c954606c5",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2112,
        640
      ],
      "parameters": {
        "width": 920,
        "height": 1400,
        "content": "## Voice and text calendar assistant on Telegram with Gmail follow-up\n\nSend a message or a voice note to a Telegram bot and an orchestrator agent books, reschedules, or cancels Google Calendar events for you. It resolves attendee names to email addresses against your Google Workspace directory, attaches a Google Meet link, and drafts a confirmation email in Gmail for any external attendee.\n\n## Who it is for\n\nAnyone who lives in Telegram and wants to run their calendar by voice or chat instead of opening a calendar UI. Useful for founders, consultants, and account managers who book meetings with people outside their own company and want a confirmation email drafted without extra work.\n\n## How it works\n\n1. A Telegram trigger receives every incoming message. An If node checks whether the message carries a voice file.\n2. Voice notes are downloaded and transcribed to text with Whisper. Plain text messages skip straight past. Both paths converge on the same field so the agent sees one consistent input.\n3. The orchestrator agent reads the request and decides what to do. It resolves any attendee mentioned by name into an email address using the Google Workspace directory tool before delegating anything.\n4. Calendar work is delegated to a Calendar sub-agent that owns four tools: create, update, look up, and delete events. Multiple attendees are added by creating the event with the first attendee, then updating once per remaining attendee.\n5. Deletions are never executed blind. The Calendar sub-agent looks the event up and reports it back, the orchestrator asks you to confirm, and only then is the delete carried out.\n6. When an event includes an attendee outside your own domain, a Gmail sub-agent drafts a confirmation email. It only ever creates a draft, it never sends.\n7. The orchestrator replies in Telegram HTML, in whatever language you wrote in.\n\n## How to set up\n\n1. Create a Telegram bot with BotFather and attach its credential to the trigger, the voice download node, and the send node.\n2. Attach a Google Calendar OAuth2 credential to the four calendar tool nodes. The calendar is set to primary, which resolves to the calendar of whichever account you connect, so there is nothing to edit.\n3. Attach a Gmail OAuth2 credential to both Gmail tool nodes, and a Google Workspace Admin credential to the directory lookup node. The directory lookup needs an admin account on a Workspace domain, skip that node if you are on a personal Gmail account.\n4. Attach an OpenRouter credential to both model nodes, and an OpenAI credential to the transcription node.\n5. Open both agent system messages and replace yourcompany.com with your own email domain. This is what decides who counts as an external attendee.\n6. Both system messages hardcode Asia/Ho_Chi_Minh as the timezone. Change it to your own zone, and change the +07:00 offset instruction to match.\n\n## Requirements\n\n- Telegram bot token\n- Google Calendar and Gmail OAuth2 credentials\n- Google Workspace Admin credential, only for directory name lookup\n- OpenAI API key for voice transcription\n- OpenRouter API key, or swap both model nodes for any other provider\n\n## How to customize\n\n- Swap either model node. The orchestrator does the harder reasoning, the sub-agents can run on a cheaper model.\n- Remove the Gmail sub-agent entirely if you do not want confirmation drafts.\n- Change the Gmail sub-agent from creating a draft to sending directly, if you are comfortable with that.\n- Replace the Telegram trigger and send nodes with Slack, WhatsApp, or a chat trigger, leaving everything else unchanged."
      },
      "typeVersion": 1
    },
    {
      "id": "bdd43517-bbba-48db-bc72-48732b1882ff",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3040,
        624
      ],
      "parameters": {
        "color": 7,
        "width": 900,
        "height": 872,
        "content": "## 1. Receive and normalise the message\n\nThe Telegram trigger fires on every incoming message. **Text or Voice?** checks whether the message carries a voice file.\n\nVoice notes go down the top path: **Download Voice File** pulls the audio from Telegram and **Transcribe Voice to Text** converts it with Whisper. Text messages take the bottom path through **Extract Text Message**.\n\nBoth paths end up producing the same `text` field, so the agent downstream only ever deals with one shape of input."
      },
      "typeVersion": 1
    },
    {
      "id": "086fbce6-b8a6-4ac3-8c2f-73f56668ac1f",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3968,
        624
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 864,
        "content": "## 2. Orchestrate\n\n**Orchestrator Agent** decides what the request needs and delegates. It never passes a person's name to the calendar, it calls **Get Contacts** first to turn that name into an email address from the Workspace directory.\n\nIt also owns the confirmation step for deletes, and decides when an external attendee warrants a Gmail draft.\n\nMemory is keyed on the Telegram chat ID, so each conversation keeps its own context."
      },
      "typeVersion": 1
    },
    {
      "id": "f967d2e5-fd32-4e7e-bd45-99afd3c7299f",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4480,
        848
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 368,
        "content": "## 3. Reply\n\nThe answer goes back to the same Telegram chat in HTML, in whichever language the user wrote in."
      },
      "typeVersion": 1
    },
    {
      "id": "3ef5e6f6-a016-4811-abe1-16afe8cda5f4",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3712,
        1536
      ],
      "parameters": {
        "color": 7,
        "width": 620,
        "height": 680,
        "content": "## 4. Calendar sub-agent\n\nOwns every calendar action. Attendee emails arrive already resolved, it never guesses one.\n\nMultiple attendees are handled by creating the event with the first attendee, then calling **Update Calendar** once per remaining attendee using the returned event ID. A Google Meet link is attached on creation.\n\n**Delete Event** is never called speculatively. The sub-agent looks the event up and hands it back for confirmation first."
      },
      "typeVersion": 1
    },
    {
      "id": "73a79bee-744b-4123-ab7c-d7e63d1844ef",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4368,
        1536
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 680,
        "content": "## 5. Gmail sub-agent\n\nDrafts a confirmation email when an event includes an attendee outside your own domain.\n\nThis sub-agent only ever creates drafts, it never sends. Nothing leaves the mailbox without you opening it."
      },
      "typeVersion": 1
    },
    {
      "id": "8e064b8e-1f18-4d65-9b81-c2fbb09dcb37",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4176,
        2272
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 380,
        "content": "## Shared model and memory\n\nBoth sub-agents run on the same cheaper model and share one memory buffer, also keyed on the Telegram chat ID."
      },
      "typeVersion": 1
    },
    {
      "id": "1659b657-e9a0-4dea-8ec4-ca169f0bc386",
      "name": "Orchestrator - Claude Sonnet 4.6",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        4016,
        1232
      ],
      "parameters": {
        "model": "anthropic/claude-sonnet-4.6",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": "epYL11FUKoUr5IdY",
    "timeSavedMode": "fixed",
    "availableInMCP": true,
    "executionOrder": "v1"
  },
  "versionId": "fad68028-5e66-4a03-9da3-a494be734b48",
  "nodeGroups": [],
  "connections": {
    "Get Events": {
      "ai_tool": [
        [
          {
            "node": "Calendar Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Agent": {
      "ai_tool": [
        [
          {
            "node": "Orchestrator Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Create Event": {
      "ai_tool": [
        [
          {
            "node": "Calendar Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Delete Event": {
      "ai_tool": [
        [
          {
            "node": "Calendar Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Get Contacts": {
      "ai_tool": [
        [
          {
            "node": "Orchestrator Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Calendar Agent": {
      "ai_tool": [
        [
          {
            "node": "Orchestrator Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Text or Voice?": {
      "main": [
        [
          {
            "node": "Download Voice File",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Extract Text Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sub-agent Model": {
      "ai_languageModel": [
        [
          {
            "node": "Gmail Agent",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Calendar Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Update Calendar": {
      "ai_tool": [
        [
          {
            "node": "Calendar Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Sub-agent Memory": {
      "ai_memory": [
        [
          {
            "node": "Gmail Agent",
            "type": "ai_memory",
            "index": 0
          },
          {
            "node": "Calendar Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Create Gmail Draft": {
      "ai_tool": [
        [
          {
            "node": "Gmail Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Orchestrator Agent": {
      "main": [
        [
          {
            "node": "Send Response to Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Voice File": {
      "main": [
        [
          {
            "node": "Transcribe Voice to Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Orchestrator Memory": {
      "ai_memory": [
        [
          {
            "node": "Orchestrator Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Extract Text Message": {
      "main": [
        [
          {
            "node": "Orchestrator Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Gmail Messages": {
      "ai_tool": [
        [
          {
            "node": "Gmail Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Transcribe Voice to Text": {
      "main": [
        [
          {
            "node": "Orchestrator Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive User Input (Telegram)": {
      "main": [
        [
          {
            "node": "Text or Voice?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Orchestrator - Claude Sonnet 4.6": {
      "ai_languageModel": [
        [
          {
            "node": "Orchestrator Agent",
            "type": "ai_languageModel",
            "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

Send a text or voice message to a Telegram bot and it books, reschedules, or cancels Google Calendar events for you. It resolves attendee names to email addresses, attaches a Meet link, and drafts a Gmail confirmation for external guests. A Telegram trigger receives every…

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

Telegram Trigger receives incoming messages (text, voice, photo, document). Switch routes by message type to appropriate processors: Text → forwarded as-is. Voice → downloaded and sent to Transcribe a

Memory Buffer Window, Telegram Trigger, Telegram +12
AI & RAG

Transform your Telegram messenger into a powerful, multi-modal personal or team assistant. This n8n workflow creates an intelligent agent that can understand text, voice, images, and documents, and ta

Memory Buffer Window, Telegram Trigger, Telegram +10
AI & RAG

Personal Assistant. Uses memoryBufferWindow, agent, agentTool, httpRequestTool. Event-driven trigger; 77 nodes.

Memory Buffer Window, Agent, Agent Tool +15
AI & RAG

Jarvis is a powerful multi-agent productivity assistant built in n8n. It works directly from Telegram and can understand both text messages and voice notes.

Telegram Trigger, Telegram, OpenAI +10
AI & RAG

Inbox Guardian. Uses gmailTrigger, lmChatOpenAi, agent, textClassifier. Event-driven trigger; 66 nodes.

Gmail Trigger, OpenAI Chat, Agent +12