This workflow follows the Agent → Gmail Tool 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 →
{
"nodes": [
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "14Qy5R25xTUGMrSrhi9NtSq6G2vXJ9Z00_2yh8NMw4Lc",
"mode": "list",
"cachedResultName": "n8n course",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/14Qy5R25xTUGMrSrhi9NtSq6G2vXJ9Z00_2yh8NMw4Lc/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": 254224309,
"mode": "list",
"cachedResultName": "Feuille 6",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/14Qy5R25xTUGMrSrhi9NtSq6G2vXJ9Z00_2yh8NMw4Lc/edit#gid=254224309"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"NAME": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('NAME', ``, 'string') }}",
"AGE ": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('AGE_', ``, 'string') }}",
"NUM": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('NUM', ``, 'string') }}",
"SERVICE": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('SERVICE', ``, 'string') }}",
"DATE": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('DATE', ``, 'string') }}",
"TIME": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('TIME', ``, 'string') }}",
"NOTES": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('NOTES', ``, 'string') }}"
},
"matchingColumns": [],
"schema": [
{
"id": "NAME",
"displayName": "NAME",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "AGE ",
"displayName": "AGE ",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "NUM",
"displayName": "NUM",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "SERVICE",
"displayName": "SERVICE",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "DATE",
"displayName": "DATE",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "TIME",
"displayName": "TIME",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "NOTES",
"displayName": "NOTES",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.7,
"position": [
880,
1104
],
"id": "ab3e685e-0d2c-40fd-9f6c-8a1e9bfe8819",
"name": "Append row in sheet in Google Sheets",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "getAll",
"calendar": {
"__rl": true,
"value": "yacine25.aa@gmail.com",
"mode": "list",
"cachedResultName": "yacine25.aa@gmail.com"
},
"timeMin": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('After', ``, 'string') }}",
"timeMax": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Before', ``, 'string') }}",
"options": {}
},
"type": "n8n-nodes-base.googleCalendarTool",
"typeVersion": 1.3,
"position": [
1056,
1056
],
"id": "2ccf11c1-4307-45de-ac39-b02234f657eb",
"name": "Get many events in Google Calendar",
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"calendar": {
"__rl": true,
"value": "yacine25.aa@gmail.com",
"mode": "list",
"cachedResultName": "yacine25.aa@gmail.com"
},
"start": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start', ``, 'string') }}",
"end": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('End', ``, 'string') }}",
"additionalFields": {
"description": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Description', ``, 'string') }}",
"summary": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Summary', ``, 'string') }}"
}
},
"type": "n8n-nodes-base.googleCalendarTool",
"typeVersion": 1.3,
"position": [
1216,
976
],
"id": "4507f209-421e-46e6-8934-d8beaea195e3",
"name": "Create an event in Google Calendar",
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"sendTo": "yacine25.aa@gmail.com",
"subject": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Subject', ``, 'string') }}",
"emailType": "text",
"message": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Message', ``, 'string') }}",
"options": {
"appendAttribution": false
}
},
"type": "n8n-nodes-base.gmailTool",
"typeVersion": 2.2,
"position": [
608,
1184
],
"id": "1fe5f772-baf0-4af2-b2f2-fe244d93ba4b",
"name": "notify a human ",
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const item = $input.first();\n\nif (!item.binary || !item.binary.data) {\n throw new Error('No audio binary found in field data.');\n}\n\nitem.binary.data.fileName = 'telegram_voice.ogg';\nitem.binary.data.mimeType = 'audio/ogg';\n\nreturn [item];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
80,
672
],
"id": "d1bed7cd-6cc6-4456-8d2a-092c94068bdb",
"name": "Code in JavaScript"
},
{
"parameters": {
"chatId": "={{$json.chat_id}}",
"text": "={{$json.ai_reply}}",
"additionalFields": {
"appendAttribution": false
}
},
"id": "4a725456-a732-488b-ae4b-ddb01743c5d4",
"name": "Send Telegram Reply",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
1104,
688
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const current = $input.first().json;\nlet original = {};\ntry { original = $('Normalize Incoming').first().json; } catch (e) { original = current; }\n\nlet reply = '';\nif (typeof current.output === 'string') reply = current.output;\nif (!reply && typeof current.text === 'string') reply = current.text;\nif (!reply && typeof current.response === 'string') reply = current.response;\nif (!reply && typeof current.output_text === 'string') reply = current.output_text;\n\nif (!reply && Array.isArray(current.output)) {\n for (const out of current.output) {\n if (typeof out === 'string') reply += out;\n if (Array.isArray(out.content)) {\n for (const c of out.content) {\n if (typeof c.text === 'string') reply += c.text;\n if (typeof c.output_text === 'string') reply += c.output_text;\n }\n }\n }\n}\n\nif (!reply) reply = 'Sorry, I could not generate a reply. Please try again.';\n\nreturn [{\n json: {\n ...original,\n ai_reply: String(reply)\n }\n}];"
},
"id": "6cd788ae-32a2-4193-bf38-536d9406b393",
"name": "Extract Agent Reply",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
800,
688
]
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{$json.chat_id}}",
"contextWindowLength": 15
},
"id": "b9082473-85aa-4fa4-90bc-8637ca34ff46",
"name": "Simple Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.4,
"position": [
448,
1184
]
},
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-4.1",
"mode": "list",
"cachedResultName": "gpt-4.1"
},
"builtInTools": {},
"options": {}
},
"id": "326fab13-e0b7-4ca8-a7b4-a0e627c707dc",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.3,
"position": [
320,
1088
],
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"promptType": "define",
"text": "={{ $json.agent_prompt }}",
"options": {
"systemMessage": "Act as an AI dental practice assistant with 20 years\u2019 experience specializing in Algerian dental practice management for H Dental Cabinets. Handle new client appointment requests in Algerian Derja (Arabic script) with all dental service names strictly in FRENCH ONLY. Automate the complete appointment booking flow: greeting, gathering & confirming info, verifying availability (dateday/holiday/work hours), handling corrections/persistence, receiving explicit confirmation, and internally preparing the appointment record. For every workflow step, first reason through and verify all required logic before outputting anything to the client.\n- Internally, always: collect necessary information, validate and cross-check (especially for date, Arabic day, work hours, holidays), and only then proceed.\n- Workflow order must never skip, combine, or reorder steps. Never present a conclusion before all internal reasoning/validation.\n- All output shown to the client must be in Algerian Derja (Arabic script), friendly with appropriate emojis. Service names are always in French.\n- Never display, mention, or reference any internal JSON, data structure, or system steps. Only finalize the appointment after the client confirms all details are correct.\n- Continue prompting for missing or incorrect information, looping persistently, until all required data is obtained per the workflow.\n\n**Workflow Steps**\n\n1. **Greeting & Services Announcement**\n - Welcome the client in Algerian Derja with emojis.\n - List all available services (service names in French only).\n2. **Information Gathering**\n - Sequentially ask for and individually confirm each of: \n - Name (\u0627\u0644\u0625\u0633\u0645), \n - Age (\u0627\u0644\u0639\u0645\u0631), \n - Phone (\u0631\u0642\u0645 \u0627\u0644\u0647\u0627\u062a\u0641), \n - SERVICE (in French only),\n - Any extra notes (\u0645\u0644\u0627\u062d\u0638\u0627\u062a \u0625\u0636\u0627\u0641\u064a\u0629 \u0644\u0644\u0637\u0628\u064a\u0628).\n3. **Availability & Holiday/Hours Validation**\n - Ask for preferred date/time, and state the working hours: \"09:00\u201317:00\".\n - Internally validate that:\n - The requested time is within hours,\n - The Arabic weekday matches the requested Gregorian date,\n - The date is not a holiday/closed (\"Ferm\u00e9\", \"Independence Day\", etc.).\n - If any error:\n - Politely inform and prompt for a new choice (give a sample polite refusal for each).\n - **Special Check**: For \"vendredi 3/7/2026\" and \"samedi 4/7/2026\", days/dates must match exactly as stated; never mismatch weekday & date.\n4. **Proposed Slot & Explicit Client Confirmation**\n - Propose only open/verified date and time (format: e.g., \"4/7/2026\", \"15:00\").\n - Ask explicitly for confirmation (e.g., \"\u062a\u0623\u0643\u062f \u0627\u0644\u062d\u062c\u0632 \u062a\u0627\u0639\u0643\u061f\").\n - Proceed to make internal appointment only after explicit client confirmation.\n5. **Appointment Record Creation (INTERNAL ONLY)**\n - After all checks and explicit confirmation, prepare via internal JSON:\n {\n \"NAME\": \"[\u0627\u0644\u0625\u0633\u0645]\",\n \"AGE\": \"[\u0627\u0644\u0639\u0645\u0631]\",\n \"NUM\": \"[\u0631\u0642\u0645 \u0627\u0644\u0647\u0627\u062a\u0641]\",\n \"SERVICE\": \"[SERVICE in French]\",\n \"DATE\": \"[Arabic day name + date, e.g., \u0627\u0644\u062e\u0645\u064a\u0633 4/7/2024]\",\n \"TIME\": \"[24-hour time, e.g., 13:00]\",\n \"NOTES\": \"[\u0645\u0644\u0627\u062d\u0638\u0627\u062a \u0625\u0636\u0627\u0641\u064a\u0629 \u0644\u0644\u0637\u0628\u064a\u0628]\"\n }\n - Do NOT show, mention, or reference this record to the client.\n6. **Optional Human Handoff**\n - If requested by the client, inform staff via staff Gmail with all collected details.\n\n**Output Format and Rules**\n- All client interaction: Algerian Derja (Arabic script) + friendly emojis.\n- All dental service names: French ONLY (never Arabic).\n- Dates to client: numeric, no Arabic weekday displayed (e.g., \"4/7/2026\").\n- Never output any internal JSON, code, or data structures.\n- Appointment is finalized only after explicit client confirmation and all verifications.\n- Persist and loop for corrections, clarifications, and workflow completion.\n\n**Reasoning & Output Order**\n- For each step, first reason and verify internally (all necessary details, checks, logic).\n- Only after full reasoning, provide the client-facing output (never start with conclusions).\n- Maintain strict original workflow order at all times.\n\n**High-Quality Example Client Flow**\n\n*Greeting & Info Gathering*\n- \u0623\u0647\u0644\u0627 \u0648\u0633\u0647\u0644\u0627 \ud83d\ude0a! \u0645\u0631\u062d\u0628\u0627 \u0628\u064a\u0643 \u0641\u064a H Dental Cabinets \ud83e\uddb7. \u0627\u0644\u062e\u062f\u0645\u0627\u062a \u0627\u0644\u0645\u062a\u0648\u0641\u0631\u0629 \u0639\u0646\u062f\u0646\u0627: D\u00e9tartrage, Blanchiment, Plombage. \u0645\u0645\u0643\u0646 \u062a\u0639\u0637\u064a\u0646\u064a \u0627\u0644\u0625\u0633\u0645\u060c \u0627\u0644\u0639\u0645\u0631\u060c \u0631\u0642\u0645 \u0627\u0644\u0647\u0627\u062a\u0641\u060c \u0648 \u0627\u0644\u062e\u062f\u0645\u0629 \u0644\u064a \u062a\u062d\u062a\u0627\u062c\u0647\u0627\u061f (\u0628\u0627\u0644\u0641\u0631\u0646\u0633\u064a)\u060c \u0648\u0625\u0630\u0627 \u0643\u0627\u0646 \u0639\u0646\u062f\u0643 \u0645\u0644\u0627\u062d\u0638\u0627\u062a \u0644\u0644\u0637\u0628\u064a\u0628\u061f\n\n*Request/Time/Holiday Handling*\n- \u0648\u0627\u0634 \u0645\u0646 \u0646\u0647\u0627\u0631 \u0648 \u0648\u0642\u062a \u062a\u062d\u0628 \u062a\u0632\u0648\u0631\u0646\u0627\u061f \u0646\u0641\u062a\u062d\u0648 \u0645\u0646 09:00 \u062d\u062a\u0649 17:00.\n- (If out-of-hours) \u0633\u0645\u062d\u0644\u064a\u060c \u0646\u0642\u062f\u0631\u0648 \u0646\u062e\u062f\u0645\u0648\u0643 \u063a\u064a\u0631 \u0628\u064a\u0646 09:00 \u0648 17:00. \u062a\u0639\u0637\u064a\u0646\u064a \u0648\u0642\u062a \u0622\u062e\u0631\u061f\n- (If holiday/closed) \u0633\u0645\u062d\u0644\u064a\u060c \u0647\u062f\u0627\u0643 \u0627\u0644\u0646\u0647\u0627\u0631 \u0631\u0627\u0647 \u0645\u063a\u0644\u0648\u0642 \u0628\u0633\u0628\u0628 [Ferm\u00e9/Independence Day]\u060c \u062a\u0642\u062f\u0631 \u062a\u0639\u0637\u064a\u0646\u064a \u0646\u0647\u0627\u0631 \u0622\u062e\u0631\u061f\n\n*Proposed Slot/Confirmation*\n- \u0631\u0627\u0647\u064a \u0645\u062a\u0648\u0641\u0631\u0629 \u0633\u0627\u0639\u0629 [15:00] \u0646\u0647\u0627\u0631 [4/7/2026]. \u062a\u0623\u0643\u062f \u0627\u0644\u062d\u062c\u0632 \u062a\u0627\u0639\u0643\u061f\n\n*(Internal Only)*\n- (record created using all client data, never shown externally)\n\n*If Staff Handoff Requested*\n- \u062d\u0627\u0636\u0631\u060c \u063a\u0627\u062f\u064a \u0646\u0639\u0644\u0645 \u0627\u0644\u0637\u0627\u0642\u0645 \u0643\u0627\u0645\u0644 \u0628\u0627\u0644\u062a\u0641\u0627\u0635\u064a\u0644.\n\n*(Realistic cases may require repeated slot negotiation, clarification on info spelling, etc. Never skip or shortcut persistence. For vendredi 3/7/2026 and samedi 4/7/2026, double-check day/date match as in user note.)*\n\n---\n**Edge Cases & Key Reminders**\n- Never offer/book hours outside 09:00\u201317:00.\n- Always internally verify Arabic weekday \u2194 Gregorian date matches in both directions.\n- Never show or refer to internal data to the client.\n- Require explicit, clear client confirmation before finalizing booking.\n- Persist (loop, recheck, clarify) until every step is satisfied.\n\n---\n**REMINDER:** \nAt every step, first reason thoroughly and verify all criteria internally; only then interact with the client in sequence, never skipping steps or issuing conclusions before all necessary checks are made. Finalize appointments only with explicit confirmation and all steps fully satisfied.\n\n[Task: Persistently automate Algerian dental appointment booking in Derja (Arabic script) per all workflow and validation/output rules above, always reasoning before response, never showing internal details, and handling date/day matching rigorously.]"
}
},
"id": "611f3089-4df4-43e8-8dbe-0f38558e6d6b",
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3.1,
"position": [
400,
688
]
},
{
"parameters": {
"jsCode": "const original = $input.first().json;\nreturn [{\n json: {\n ...original,\n agent_prompt: 'The client sent an unsupported message type. Reply politely that you can read text, images, and audio.'\n }\n}];"
},
"id": "b8f7dcea-02a2-4577-beed-c17e30889923",
"name": "Prepare Unknown For Agent",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
16,
1504
]
},
{
"parameters": {
"jsCode": "const current = $input.first().json;\nlet original = {};\ntry { original = $('Normalize Incoming').first().json; } catch (e) { original = current; }\n\nconst transcript = current.text || '';\n\nreturn [{\n json: {\n ...original,\n message_type: 'audio',\n audio_transcript: transcript,\n agent_prompt:\n `Client sent an audio message.\\n\\n` +\n `Transcript:\\n${transcript}\\n\\n` +\n `Reply naturally as a helpful business assistant. Use the Business Info Sheet tool when useful. If the audio includes lead details, call the Save Lead tool.`\n }\n}];"
},
"id": "b95df9ca-1157-4e9b-a134-cb40c0a8bcb8",
"name": "Prepare Audio For Agent",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
96,
944
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/audio/transcriptions",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "openAiApi",
"sendBody": true,
"contentType": "multipart-form-data",
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "gpt-4o-transcribe"
},
{
"parameterType": "formBinaryData",
"name": "file",
"inputDataFieldName": "data"
}
]
},
"options": {}
},
"id": "4845b81b-2e38-4339-a28e-acb4d89169b4",
"name": "Transcribe Audio",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-176,
1072
],
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "file",
"fileId": "={{$json.audio_file_id}}",
"additionalFields": {
"mimeType": "audio/ogg"
}
},
"id": "258ea8c0-f73a-48ba-bf8b-99d5e1c8c61a",
"name": "Telegram Download Audio",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
-176,
656
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const current = $input.first().json;\nconst audioFileId = current.audio_file_id || '';\n\nif (!audioFileId) {\n throw new Error('No Telegram audio file_id found. Send a real Telegram voice/audio message.');\n}\n\nreturn [{ json: { ...current, audio_file_id: audioFileId } }];"
},
"id": "0f682c3f-71d3-4e11-bcd8-1c591f6564f1",
"name": "Get Audio File ID",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-384,
624
]
},
{
"parameters": {
"jsCode": "const current = $input.first().json;\nlet original = {};\ntry { original = $('Normalize Incoming').first().json; } catch (e) { original = current; }\n\nlet imageSummary = current.output_text || '';\n\nif (!imageSummary && Array.isArray(current.output)) {\n for (const out of current.output) {\n if (Array.isArray(out.content)) {\n for (const c of out.content) {\n if (typeof c.text === 'string') imageSummary += c.text;\n if (typeof c.output_text === 'string') imageSummary += c.output_text;\n }\n }\n }\n}\n\nif (!imageSummary && typeof current.text === 'string') imageSummary = current.text;\nif (!imageSummary) imageSummary = 'The image was received, but the image analysis node did not return a clear description.';\n\nreturn [{\n json: {\n ...original,\n image_summary: imageSummary,\n agent_prompt:\n `Client sent an image.\\n\\n` +\n `Caption:\\n${original.message_text || ''}\\n\\n` +\n `Image summary:\\n${imageSummary}\\n\\n` +\n `Reply as a helpful business assistant. Use the Business Info Sheet tool for company facts, and the Save Lead tool if this looks like a sales inquiry with contact details.`\n }\n}];"
},
"id": "f4a04800-cf2a-4519-b28e-23811f524142",
"name": "Prepare Image For Agent",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
16,
352
]
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/responses",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "openAiApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ { model: 'gpt-4.1', input: [{ role: 'user', content: [{ type: 'input_text', text: 'Describe this client image for a business assistant. Include visible objects, client intent, and any text in the image. Caption: ' + ($json.message_text || '') }, { type: 'input_image', image_url: $json.image_data_url }] }] } }}",
"options": {}
},
"id": "eee1cb09-03cb-40ad-b871-8540d82c7d00",
"name": "Describe Image",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-144,
304
],
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const buffer = await this.helpers.getBinaryDataBuffer(0, 'image');\nconst mimeType = $input.first().binary?.image?.mimeType || 'image/jpeg';\nconst imageDataUrl = `data:${mimeType};base64,${buffer.toString('base64')}`;\nreturn [{ json: { ...$input.first().json, image_data_url: imageDataUrl }, binary: $input.first().binary }];"
},
"id": "33ff1875-10cc-4a3f-812c-73cebb4be92d",
"name": "Build Image Data URL",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-336,
416
]
},
{
"parameters": {
"jsCode": "const original = $input.first().json;\nreturn [{\n json: {\n ...original,\n agent_prompt:\n `Client message:\\n${original.message_text || ''}\\n\\n` +\n `Reply as a helpful business assistant. Keep it short. ` +\n `Use the Business Info Sheet tool for pricing, hours, services, policies, or FAQs. ` +\n `If the client gives name, phone, email, need, or urgency, call the Save Lead tool.`\n }\n}];"
},
"id": "3607db38-d51c-453e-ba9c-b2330a402da7",
"name": "Prepare Text For Agent",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-64,
-48
]
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"conditions": [
{
"leftValue": "={{$json.message_type}}",
"rightValue": "text",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and",
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
}
}
},
{
"conditions": {
"conditions": [
{
"leftValue": "={{$json.message_type}}",
"rightValue": "image",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and",
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
}
}
},
{
"conditions": {
"conditions": [
{
"leftValue": "={{$json.message_type}}",
"rightValue": "audio",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and",
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
}
}
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"id": "79df4105-3dbd-48d5-b92c-2b1508a2618c",
"name": "Switch Type",
"type": "n8n-nodes-base.switch",
"typeVersion": 3,
"position": [
-832,
544
]
},
{
"parameters": {
"jsCode": "const item = $input.first();\nconst msg = item.json.message || item.json;\nconst binary = item.binary || {};\nconst keys = Object.keys(binary);\n\nlet messageType = 'unknown';\nlet hasImage = false;\nlet hasAudio = false;\n\nconst text = msg.text || msg.caption || '';\nconst audioFileId = msg.voice?.file_id || msg.audio?.file_id || msg.document?.file_id || '';\nconst imageFileId = msg.photo?.[msg.photo.length - 1]?.file_id || '';\n\nif (audioFileId) hasAudio = true;\nif (imageFileId) hasImage = true;\n\nfor (const key of keys) {\n const b = binary[key];\n if (!b) continue;\n\n const mime = String(b.mimeType || '').toLowerCase();\n const fileName = String(b.fileName || '').toLowerCase();\n\n if (mime.startsWith('image/') || fileName.match(/\\.(jpg|jpeg|png|webp)$/)) {\n hasImage = true;\n binary.image = b;\n }\n\n if (mime.startsWith('audio/') || mime.includes('ogg') || mime.includes('opus') || fileName.match(/\\.(ogg|oga|opus|mp3|m4a|wav)$/)) {\n hasAudio = true;\n binary.audio = b;\n }\n}\n\nif (hasAudio) messageType = 'audio';\nelse if (hasImage) messageType = 'image';\nelse if (text) messageType = 'text';\n\nreturn [{\n json: {\n chat_id: msg.chat?.id,\n username: msg.from?.username || '',\n first_name: msg.from?.first_name || '',\n message_id: msg.message_id,\n message_text: text,\n message_type: messageType,\n audio_file_id: audioFileId,\n image_file_id: imageFileId,\n timestamp: new Date().toISOString()\n },\n binary\n}];"
},
"id": "3170b76b-335f-454e-9006-2d736274d14b",
"name": "Normalize Incoming",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1072,
560
]
},
{
"parameters": {
"updates": [
"message"
],
"additionalFields": {
"download": true
}
},
"id": "188b6196-1f6d-43be-961d-4b3a207234a0",
"name": "Telegram Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.2,
"position": [
-1328,
560
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Append row in sheet in Google Sheets": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Get many events in Google Calendar": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Create an event in Google Calendar": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"notify a human ": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Transcribe Audio",
"type": "main",
"index": 0
}
]
]
},
"Extract Agent Reply": {
"main": [
[
{
"node": "Send Telegram Reply",
"type": "main",
"index": 0
}
]
]
},
"Simple Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Extract Agent Reply",
"type": "main",
"index": 0
}
]
]
},
"Prepare Unknown For Agent": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Prepare Audio For Agent": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Transcribe Audio": {
"main": [
[
{
"node": "Prepare Audio For Agent",
"type": "main",
"index": 0
}
]
]
},
"Telegram Download Audio": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"Get Audio File ID": {
"main": [
[
{
"node": "Telegram Download Audio",
"type": "main",
"index": 0
}
]
]
},
"Prepare Image For Agent": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Describe Image": {
"main": [
[
{
"node": "Prepare Image For Agent",
"type": "main",
"index": 0
}
]
]
},
"Build Image Data URL": {
"main": [
[
{
"node": "Describe Image",
"type": "main",
"index": 0
}
]
]
},
"Prepare Text For Agent": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Switch Type": {
"main": [
[
{
"node": "Prepare Text For Agent",
"type": "main",
"index": 0
}
],
[
{
"node": "Build Image Data URL",
"type": "main",
"index": 0
}
],
[
{
"node": "Get Audio File ID",
"type": "main",
"index": 0
}
],
[
{
"node": "Prepare Unknown For Agent",
"type": "main",
"index": 0
}
]
]
},
"Normalize Incoming": {
"main": [
[
{
"node": "Switch Type",
"type": "main",
"index": 0
}
]
]
},
"Telegram Trigger": {
"main": [
[
{
"node": "Normalize Incoming",
"type": "main",
"index": 0
}
]
]
}
},
"meta": {
"templateCredsSetupCompleted": true
}
}
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.
gmailOAuth2googleCalendarOAuth2ApigoogleSheetsOAuth2ApiopenAiApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Dental-Appointment-Bot. Uses googleSheetsTool, googleCalendarTool, gmailTool, telegram. Event-driven trigger; 22 nodes.
Source: https://gist.github.com/Ahmed1380/1a2c1226f1ec556a9f45d524c89b7e7b — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
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
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
Personal Assistant. Uses memoryBufferWindow, agent, agentTool, httpRequestTool. Event-driven trigger; 77 nodes.
Digital marketers, content creators, social media managers, and businesses who want to use AI marketing automation for YouTube Shorts without spending hours on production. This AI workflow helps anyon
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.