This workflow follows the HTTP Request → Telegram 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 →
{
"name": "n8n_flow",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "aa46a723-619e-42e9-8e51-49ba51813718",
"authentication": "headerAuth",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
192,
1216
],
"id": "0590c5f6-8164-4ee0-b5c0-91211b88d496",
"name": "Webhook-homework",
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "080ee5fb-782f-47d6-9d64-98ec8e8c52f2",
"name": "student_user_id",
"value": "={{ $json.body.student_user_id }}",
"type": "string"
},
{
"id": "78fcca79-e818-4b3c-803f-94deda4f3104",
"name": "course_id",
"value": "={{ $json.body.course_numeric_id }}",
"type": "string"
},
{
"id": "969420d2-791b-40a4-924d-ec7e332d3d6b",
"name": "lesson_num",
"value": "={{ $json.body.lesson_num }}",
"type": "string"
},
{
"id": "2d2c5f78-ecaf-408d-acdb-ff6d7a4d086d",
"name": "admin_chat_id",
"value": "={{ $json.body.admin_group_id }}",
"type": "string"
},
{
"id": "5b60f83f-1667-4f43-9ab5-e7aad6d988f1",
"name": "target_message_id",
"value": "={{ $json.body.original_admin_message_id || $json.body.admin_message_id }}",
"type": "string"
},
{
"id": "c324098f-8660-4435-acef-09352a9a0a45",
"name": "student_name",
"value": "={{ ($json.body.user_fullname || $json.body.student_name || '\u0421\u0442\u0443\u0434\u0435\u043d\u0442').split(' ')[0] }}",
"type": "string"
},
{
"id": "753df8c9-e2c2-4ea6-aef4-5608ef8f92b1",
"name": "hw_text",
"value": "={{ $json.body.homework_text || '[\u0422\u0435\u043a\u0441\u0442 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442]' }}",
"type": "string"
},
{
"id": "2653261d-4f8b-494d-8bb3-975e69a96b0f",
"name": "hw_file_id",
"value": "={{ $json.body.homework_file_id || '' }}",
"type": "string"
},
{
"id": "2168a0e7-3cb9-44a2-b21c-85050c3405b2",
"name": "lesson_desc",
"value": "={{ $json.body.lesson_assignment_description ||'\u041e\u0446\u0435\u043d\u0438\u0442\u044c \u0414\u0417' }}",
"type": "string"
}
]
},
"includeOtherFields": true,
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
400,
1216
],
"id": "9ad9c3dc-01f3-4787-9b46-d4ff55e43163",
"name": "Edit Fields",
"alwaysOutputData": true
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "5b1189a6-6cee-49af-ad61-c91de051a0ea",
"leftValue": "={{ $('Edit Fields').first().json.hw_file_id }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
608,
1216
],
"id": "ec3fb10a-4e02-43f7-81e6-c7b5c52fc219",
"name": "If"
},
{
"parameters": {
"jsCode": "// \u0422\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u0414\u0417 (\u0431\u0435\u0437 \u0444\u0430\u0439\u043b\u043e\u0432) - \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 messages content\nconst hwText = $input.first().json.hw_text || $('Edit Fields').first().json.hw_text || '[\u0422\u0435\u043a\u0441\u0442 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442]';\nconst studentName = $('Edit Fields').first().json.student_name || '\u0421\u0442\u0443\u0434\u0435\u043d\u0442';\nconst lessonDesc = $('Edit Fields').first().json.lesson_desc || '\u041e\u0446\u0435\u043d\u0438\u0442\u044c \u0414\u0417';\n\nconsole.log(`\ud83d\udcdd \u0422\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u0414\u0417 \u043e\u0442 ${studentName}`);\n\n// \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c messages content \u0442\u043e\u043b\u044c\u043a\u043e \u0441 \u0442\u0435\u043a\u0441\u0442\u043e\u043c\nconst messagesContent = [\n {\n type: \"text\",\n text: `\u0420\u0410\u0411\u041e\u0422\u0410 \u0421\u0422\u0423\u0414\u0415\u041d\u0422\u0410:\\n${hwText}`\n }\n];\n\nreturn [{\n json: {\n ...$input.first().json,\n messages_content: messagesContent\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
864,
1440
],
"id": "3abe1ac7-fa0a-4e3a-8c22-95f0bdd56e6e",
"name": "Prepare Text Messages"
},
{
"parameters": {
"resource": "file",
"fileId": "={{ $('Edit Fields').item.json.hw_file_id }}",
"additionalFields": {
"mimeType": ""
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
800,
1280
],
"id": "46f93837-0c47-4bfa-a5e3-0bd8b54bc8ad",
"name": "Get a file",
"alwaysOutputData": false,
"retryOnFail": true,
"waitBetweenTries": 200,
"maxTries": 2,
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"dataType": "string",
"value1": "={{ ['jpg', 'jpeg', 'png'].includes($binary.data.fileExtension) || $binary.data.mimeType.startsWith('image') ? 'image' :['ogg', 'mp3', 'wav', 'm4a', 'oga'].includes($binary.data.fileExtension) || $binary.data.mimeType.startsWith('audio') ? 'audio' : ['mp4', 'mov', 'avi'].includes($binary.data.fileExtension) || $binary.data.mimeType.startsWith('video') ? 'video' : 'unknown' }}",
"rules": {
"rules": [
{
"operation": "startsWith",
"value2": "image"
},
{
"operation": "startsWith",
"value2": "audio"
},
{
"operation": "startsWith",
"value2": "video"
}
]
}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 2,
"position": [
976,
1200
],
"id": "4798db87-2e90-4c51-943a-7d9f37e7607a",
"name": "Switch",
"onError": "continueErrorOutput"
},
{
"parameters": {
"jsCode": "const item = $input.first();\nif (!item || !item.binary || !item.binary.data) return[];\n\n// \u0427\u0438\u0442\u0430\u0435\u043c \u0420\u0415\u0410\u041b\u042c\u041d\u042b\u0419 \u0444\u0430\u0439\u043b \u0441 \u0434\u0438\u0441\u043a\u0430 n8n \u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u043c \u0432 Base64\nconst binaryBuffer = await this.helpers.getBinaryDataBuffer(0, 'data');\nconst actualBase64String = binaryBuffer.toString('base64');\n\n// \u0427\u0438\u043d\u0438\u043c \u043a\u0440\u0438\u0432\u043e\u0439 \u0442\u0438\u043f \u043e\u0442 \u0422\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u0430\nlet mimeType = item.binary.data.mimeType;\nif (!mimeType || mimeType.includes('octet-stream')) {\n mimeType = item.binary.data.fileExtension === 'png' ? 'image/png' : 'image/jpeg';\n}\n\nitem.json.base64_image = actualBase64String; \nitem.json.mime_type = mimeType;\nitem.json.file_type = 'image';\n\nreturn [item];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1184,
1024
],
"id": "833c517f-78d8-44c1-8303-faec9d653293",
"name": "Process Photo"
},
{
"parameters": {
"jsCode": "// \u0412\u0435\u0442\u043a\u0430 \u0410\u0423\u0414\u0418\u041e: \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 + \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445\n\nconst item = $input.first();\nif (!item || !item.binary || !item.binary.data) return[];\n\n// \u0427\u0438\u0442\u0430\u0435\u043c \u0420\u0415\u0410\u041b\u042c\u041d\u042b\u0419 \u0444\u0430\u0439\u043b \u0441 \u0434\u0438\u0441\u043a\u0430 n8n \u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u043c \u0432 Base64\nconst binaryBuffer = await this.helpers.getBinaryDataBuffer(0, 'data');\nconst actualBase64String = binaryBuffer.toString('base64');\n\nlet mimeType = item.binary.data.mimeType || 'audio/ogg';\nconst fileSize = item.binary.data.fileSize || 0;\n\nif (fileSize > 10 * 1024 * 1024) {\n item.json.audio_warning = `\u0410\u0443\u0434\u0438\u043e \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0431\u043e\u043b\u044c\u0448\u043e\u0435. \u0418\u0418 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u044c.`;\n}\n\nitem.json.audio_data = actualBase64String;\nitem.json.file_type = 'audio';\nitem.json.mime_type = mimeType;\n\nreturn [item];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1184,
1184
],
"id": "c3698a97-e0d0-4d2a-a437-f31144211bd3",
"name": "Process Audio"
},
{
"parameters": {
"jsCode": "// \u0412\u0435\u0442\u043a\u0430 \u0412\u0418\u0414\u0415\u041e (\u043a\u0440\u0443\u0436\u043e\u0447\u043a\u0438/\u0433\u043e\u043b\u043e\u0441\u043e\u0432\u044b\u0435 video/ogg): \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 + \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445\nconst item = $input.first();\nif (!item || !item.binary || !item.binary.data) return[];\n\n// \u0427\u0438\u0442\u0430\u0435\u043c \u0420\u0415\u0410\u041b\u042c\u041d\u042b\u0419 \u0444\u0430\u0439\u043b \u0441 \u0434\u0438\u0441\u043a\u0430 n8n \u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u043c \u0432 Base64\nconst binaryBuffer = await this.helpers.getBinaryDataBuffer(0, 'data');\nconst actualBase64String = binaryBuffer.toString('base64');\n\nlet mimeType = item.binary.data.mimeType || 'video/ogg';\nconst fileSize = item.binary.data.fileSize || 0;\n\nif (fileSize > 10 * 1024 * 1024) {\n item.json.file_warning = `\u0424\u0430\u0439\u043b \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0431\u043e\u043b\u044c\u0448\u043e\u0439.`;\n}\n\nitem.json.audio_data = actualBase64String;\nitem.json.file_type = 'video';\nitem.json.mime_type = mimeType;\n\nreturn [item];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1184,
1344
],
"id": "a878bc4b-0795-448b-b520-d68b4d383b08",
"name": "Process Video"
},
{
"parameters": {
"jsCode": "// \u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 messages content \u0434\u043b\u044f OpenRouter AI\nconst hwText = $input.first().json.hw_text || $('Edit Fields').first().json.hw_text || '[\u0422\u0435\u043a\u0441\u0442 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442]';\nconst audioWarning = $input.first().json.audio_warning || '';\nconst fileWarning = $input.first().json.file_warning || '';\nconst base64Image = $input.first().json.base64_image || null;\nconst mimeType = $input.first().json.mime_type || '';\nconst fileType = $input.first().json.file_type || '';\nconst audioData = $input.first().json.audio_data || null;\n\n// \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c text content\nlet textContent = `\u0420\u0410\u0411\u041e\u0422\u0410 \u0421\u0422\u0423\u0414\u0415\u041d\u0422\u0410:\\n${hwText}`;\nif (audioWarning) textContent += `\\n\\n\u26a0\ufe0f ${audioWarning}`;\nif (fileWarning) textContent += `\\n\\n\u26a0\ufe0f ${fileWarning}`;\n\n// \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c messages content array\nconst messagesContent = [\n {\n type: \"text\",\n text: textContent\n }\n];\n\n// \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c image \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c\nif (base64Image && mimeType) {\n messagesContent.push({\n type: \"image_url\",\n image_url: {\n url: `data:${mimeType};base64,${base64Image}`\n }\n });\n}\n\n// \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c audio \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c (\u0434\u043b\u044f audio \u0438 video)\nif (audioData && (fileType === 'audio' || fileType === 'video')) {\n const audioFormat = mimeType.includes('ogg') ? 'ogg' : 'wav';\n messagesContent.push({\n type: \"input_audio\",\n input_audio: {\n data: audioData,\n format: audioFormat\n }\n });\n}\n\nconsole.log('\ud83d\udcdd Messages content prepared:', JSON.stringify(messagesContent).substring(0, 200));\n\n// \ud83d\udd0d DEBUG \u0434\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438\nconsole.log('\ud83d\udd0d Prepare AI Messages DEBUG:');\nconsole.log(' base64Image:', base64Image ? '\u0415\u0421\u0422\u042c' : '\u041d\u0415\u0422');\nconsole.log(' mimeType:', mimeType || '\u041d\u0415\u0422');\nconsole.log(' fileType:', fileType || '\u041d\u0415\u0422');\nconsole.log(' audioData:', audioData ? '\u0415\u0421\u0422\u042c' : '\u041d\u0415\u0422');\nconsole.log(' messages_content:', JSON.stringify(messagesContent));\n\nreturn [{\n json: {\n ...$input.first().json,\n messages_content: messagesContent\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1424,
1184
],
"id": "eb7d45ae-9201-477b-8630-7de2300226ae",
"name": "Prepare AI Messages"
},
{
"parameters": {
"jsCode": "// \u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 JSON \u0434\u043b\u044f OpenRouter AI\n// \u0411\u0435\u0440\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u0430 \u0438\u0437 \u0441\u0430\u043c\u043e\u0433\u043e \u043d\u0430\u0447\u0430\u043b\u0430\nconst studentName = $('Edit Fields').first().json.student_name || '\u0421\u0442\u0443\u0434\u0435\u043d\u0442';\nconst lessonDesc = $('Edit Fields').first().json.lesson_desc || '\u041e\u0446\u0435\u043d\u0438\u0442\u044c \u0414\u0417';\n\n// \u0411\u0435\u0440\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b\u0438 \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u043d\u043e\u0434\u0430\u0445 (\u0442\u0435\u043a\u0441\u0442 + \u0444\u043e\u0442\u043e/\u0430\u0443\u0434\u0438\u043e)\nconst messagesContent = $input.first().json.messages_content || [];\n\nconsole.log(`\ud83e\udd16 \u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043a \u0418\u0418 \u0434\u043b\u044f ${studentName}`);\n\nreturn [{\n json: {\n // \u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f OpenRouter\n openrouter_request: {\n model: 'google/gemini-2.5-flash',\n response_format: { type: 'json_object' },\n messages: [\n {\n role: 'system',\n content: `\u0422\u044b \u043c\u0443\u0434\u0440\u044b\u0439 \u043d\u0430\u0441\u0442\u0430\u0432\u043d\u0438\u043a \u043d\u0430 \u043e\u043d\u043b\u0430\u0439\u043d-\u043a\u0443\u0440\u0441\u0435. \u0418\u043c\u044f \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u0430: ${studentName}.\\n\\n\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0437\u0430\u0434\u0430\u043d\u0438\u044f:\\n${lessonDesc}\\n\\n\u0422\u0412\u041e\u0418 \u041f\u0420\u0410\u0412\u0418\u041b\u0410:\\n1. \u041e\u0446\u0435\u043d\u0438 \u0440\u0430\u0431\u043e\u0442\u0443. \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u0444\u043e\u0442\u043e \u2014 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043b\u0438 \u043e\u043d\u043e \u0437\u0430\u0434\u0430\u043d\u0438\u044e.\\n2. \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u0430\u0443\u0434\u0438\u043e/\u0432\u0438\u0434\u0435\u043e \u2014 \u0418\u0418 \u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u0430\u0443\u0434\u0438\u043e.\\n3. \u0415\u0441\u043b\u0438 \u0440\u0430\u0431\u043e\u0442\u0430 \u043e\u0441\u043c\u044b\u0441\u043b\u0435\u043d\u043d\u0430\u044f \u2014 \u0441\u0442\u0430\u0432\u044c \"is_approved\": true. \u0411\u0435\u0441\u0441\u043c\u044b\u0441\u043b\u0435\u043d\u043d\u0430\u044f \u2014 false.\\n4. \u0422\u0432\u043e\u0439 \"feedback_text\" \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043b\u0438\u0447\u043d\u044b\u043c, \u043e\u0431\u043e\u0434\u0440\u044f\u044e\u0449\u0438\u043c, \u0441 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c\u0438 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f\u043c\u0438.\\n5. \u0415\u0441\u043b\u0438 \u0441\u0442\u0443\u0434\u0435\u043d\u0442 \u043f\u0440\u0438\u0441\u043b\u0430\u043b \u0444\u043e\u0442\u043e \u2014 \u0443\u043f\u043e\u043c\u044f\u043d\u0438 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u0434\u0435\u0442\u0430\u043b\u0438.\\n\\n\u041e\u0442\u0432\u0435\u0442 \u0421\u0422\u0420\u041e\u0413\u041e \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JSON: {\"is_approved\": boolean, \"feedback_text\": \"string\"}`\n },\n {\n role: 'user',\n content: messagesContent\n }\n ]\n }\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1632,
1360
],
"id": "d2f1f377-3c5b-471d-ab92-e820df9b36c7",
"name": "Prepare OpenRouter JSON"
},
{
"parameters": {
"method": "POST",
"url": "https://openrouter.ai/api/v1/chat/completions",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "openRouterApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "HTTP-Referer",
"value": "https://n8n.indikov.ru"
},
{
"name": "X-Title",
"value": "AntBot Homework Checker"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ $json.openrouter_request }}",
"options": {
"timeout": 60000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1872,
1536
],
"id": "eb48b57c-38bf-43fb-b9d6-392de4e4daa5",
"name": "OpenRouter AI",
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 2000,
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"jsCode": "// \u041f\u0430\u0440\u0441\u0438\u043d\u0433 \u043e\u0442\u0432\u0435\u0442\u0430 \u043e\u0442 OpenRouter\ntry {\n const rawResponse = $input.item.json.choices[0].message.content;\n \n console.log('\ud83e\udd14 \u0421\u044b\u0440\u043e\u0439 \u043e\u0442\u0432\u0435\u0442 \u0418\u0418:', rawResponse.substring(0, 200));\n \n const jsonMatch = rawResponse.match(/(\\{[\\s\\S]*\\})/);\n \n if (!jsonMatch || !jsonMatch[1]) {\n throw new Error('\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0438\u0437\u0432\u043b\u0435\u0447\u044c JSON \u0438\u0437 \u043e\u0442\u0432\u0435\u0442\u0430 \u0418\u0418');\n }\n \n const parsedData = JSON.parse(jsonMatch[1]);\n \n if (typeof parsedData.is_approved !== 'boolean') {\n throw new Error('is_approved \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c boolean');\n }\n \n if (typeof parsedData.feedback_text !== 'string') {\n throw new Error('feedback_text \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c string');\n }\n \n return {\n json: {\n is_approved: parsedData.is_approved,\n feedback_text: parsedData.feedback_text,\n ai_raw_response: rawResponse\n }\n };\n} catch (error) {\n console.error('\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430:', error.message);\n \n return {\n json: {\n is_approved: true,\n feedback_text: `\u26a0\ufe0f \u0412\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043f\u0440\u0438 \u0430\u043d\u0430\u043b\u0438\u0437\u0435 \u0418\u0418, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0437\u0430\u0434\u0430\u043d\u0438\u0435 \u043e\u0434\u043e\u0431\u0440\u0435\u043d\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438. \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435!\\n\\n(\u041e\u0448\u0438\u0431\u043a\u0430: ${error.message})`,\n ai_raw_response: $input.item.json.choices?.[0]?.message?.content || '\u041d\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430',\n parse_error: true\n }\n };\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2064,
1344
],
"id": "4105ceaf-ec08-410a-8b88-12ee76b2aa25",
"name": "Parse JSON Response",
"onError": "continueErrorOutput"
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Webhook-homework').item.json.body.callback_webhook_url_result }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-CALLBACK-SIGNATURE",
"value": "500"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "feedback_text",
"value": "={{ $('Parse JSON Response').item.json.feedback_text }}"
},
{
"name": "is_approved",
"value": "={{ $('Parse JSON Response').item.json.is_approved }}"
},
{
"name": "original_admin_message_id",
"value": "={{ $('Webhook-homework').item.json.body.original_admin_message_id || $('Webhook-homework').item.json.body.admin_message_id }}"
},
{
"name": "student_user_id",
"value": "={{ $('Edit Fields').item.json.student_user_id }}"
},
{
"name": "course_numeric_id",
"value": "={{ $('Webhook-homework').item.json.body.course_numeric_id }}"
},
{
"name": "lesson_num",
"value": "={{ $('Webhook-homework').item.json.body.lesson_num }}"
}
]
},
"options": {
"timeout": 30000
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2320,
1344
],
"id": "bd5ab723-2dd8-411a-90d8-676f47c96801",
"name": "HTTP Request",
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Webhook-homework').item.json.body.callback_webhook_url_result }}",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-CALLBACK-SIGNATURE",
"value": "500"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "feedback_text",
"value": "\u26a0\ufe0f \u0412\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043f\u0440\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0435 \u0414\u0417, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0437\u0430\u0434\u0430\u043d\u0438\u0435 \u043e\u0434\u043e\u0431\u0440\u0435\u043d\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438. \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435!"
},
{
"name": "is_approved",
"value": true
},
{
"name": "original_admin_message_id",
"value": "={{ $('Webhook-homework').item.json.body.original_admin_message_id || $('Webhook-homework').item.json.body.admin_message_id }}"
},
{
"name": "student_user_id",
"value": "={{ $('Edit Fields').item.json.student_user_id }}"
},
{
"name": "course_numeric_id",
"value": "={{ $('Webhook-homework').item.json.body.course_numeric_id }}"
},
{
"name": "lesson_num",
"value": "={{ $('Webhook-homework').item.json.body.lesson_num }}"
},
{
"name": "error_details",
"value": "={{ $json.error }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2320,
1504
],
"id": "de66127b-db2e-475b-9275-23351cab9e48",
"name": "HTTP Request (Error Fallback)",
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "## \ud83d\udea8 \u041e\u0428\u0418\u0411\u041a\u0410 \u0412\u041e\u0420\u041a\u0424\u041b\u041e\u0423\n\n\u0415\u0441\u043b\u0438 \u0432\u044b \u0432\u0438\u0434\u0438\u0442\u0435 \u044d\u0442\u043e \u2014 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0414\u0417.\n\n**\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u043f\u0440\u0438\u0447\u0438\u043d\u044b:**\n- OpenRouter API \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d\n- \u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0444\u0430\u0439\u043b\u0430 \u043e\u0442 \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u0430\n- \u041f\u0440\u0435\u0432\u044b\u0448\u0435\u043d \u043b\u0438\u043c\u0438\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438\n\n**\u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c:**\n1. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043b\u043e\u0433\u0438 n8n: `docker compose logs n8n`\n2. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0431\u0430\u043b\u0430\u043d\u0441 OpenRouter\n3. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0441\u043a\u0430\u0447\u0430\u043b\u0441\u044f \u0438\u0437 Telegram",
"height": 460,
"width": 592
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
2320,
1664
],
"id": "53e3f5f9-6652-435c-a425-3304a8ef7f26",
"name": "Sticky Note"
},
{
"parameters": {
"content": "## \ud83d\udcca \u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\n\n\u0412\u0441\u0435 \u044d\u0442\u0430\u043f\u044b \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043b\u043e\u0433\u0438\u0440\u0443\u044e\u0442\u0441\u044f:\n- \ud83d\udcf8 \u0424\u043e\u0442\u043e: \u0440\u0430\u0437\u043c\u0435\u0440, MIME \u0442\u0438\u043f\n- \ud83c\udfa7 \u0410\u0443\u0434\u0438\u043e: \u0440\u0430\u0437\u043c\u0435\u0440, \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u044f \u0435\u0441\u043b\u0438 >10 \u041c\u0411\n- \ud83c\udfa5 \u0412\u0438\u0434\u0435\u043e: \u0440\u0430\u0437\u043c\u0435\u0440, \u0442\u0438\u043f \u0444\u0430\u0439\u043b\u0430\n- \ud83e\udd14 \u041e\u0442\u0432\u0435\u0442 \u0418\u0418: \u043f\u0435\u0440\u0432\u044b\u0435 200 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432\n- \u274c \u041e\u0448\u0438\u0431\u043a\u0438: \u0434\u0435\u0442\u0430\u043b\u0438 \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430\n\n**\u0414\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u043b\u043e\u0433\u043e\u0432:**\n```bash\ndocker compose logs n8n | grep -E \"\ud83d\udcf8|\ud83c\udfa7|\ud83c\udfa5|\ud83e\udd14|\u274c\"\n```",
"height": 314,
"width": 350
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
992,
1536
],
"id": "e88f3566-1eb5-49ae-8481-14d6821a200c",
"name": "Sticky Note1"
}
],
"connections": {
"Webhook-homework": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"If": {
"main": [
[
{
"node": "Get a file",
"type": "main",
"index": 0
}
],
[
{
"node": "Prepare Text Messages",
"type": "main",
"index": 0
}
]
]
},
"Prepare Text Messages": {
"main": [
[
{
"node": "Prepare OpenRouter JSON",
"type": "main",
"index": 0
}
]
]
},
"Get a file": {
"main": [
[
{
"node": "Switch",
"type": "main",
"index": 0
}
]
]
},
"Switch": {
"main": [
[
{
"node": "Process Photo",
"type": "main",
"index": 0
}
],
[
{
"node": "Process Audio",
"type": "main",
"index": 0
}
],
[
{
"node": "Process Video",
"type": "main",
"index": 0
}
]
]
},
"Process Photo": {
"main": [
[
{
"node": "Prepare AI Messages",
"type": "main",
"index": 0
}
]
]
},
"Process Audio": {
"main": [
[
{
"node": "Prepare AI Messages",
"type": "main",
"index": 0
}
]
]
},
"Process Video": {
"main": [
[
{
"node": "Prepare AI Messages",
"type": "main",
"index": 0
}
]
]
},
"Prepare AI Messages": {
"main": [
[
{
"node": "Prepare OpenRouter JSON",
"type": "main",
"index": 0
}
]
]
},
"Prepare OpenRouter JSON": {
"main": [
[
{
"node": "OpenRouter AI",
"type": "main",
"index": 0
}
]
]
},
"OpenRouter AI": {
"main": [
[
{
"node": "Parse JSON Response",
"type": "main",
"index": 0
}
],
[
{
"node": "HTTP Request (Error Fallback)",
"type": "main",
"index": 0
}
]
]
},
"Parse JSON Response": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
],
[
{
"node": "HTTP Request (Error Fallback)",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"availableInMCP": false
},
"versionId": "c3d33d06-a79b-4675-80f1-30b453df6a63",
"id": "vzQALMCXBCd11Epz",
"tags": []
}
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.
httpHeaderAuthopenRouterApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
n8n_flow. Uses telegram, httpRequest. Webhook trigger; 17 nodes.
Source: https://github.com/andrjur/antbot3/blob/main/n8n_flow.json — 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.
WF_UNIFIED_LEGAL_AUTOMATION. Uses googleSheets, httpRequest, telegram, telegramTrigger. Webhook trigger; 53 nodes.
qualiopi. Uses airtable, telegram, emailSend, httpRequest. Webhook trigger; 51 nodes.
PsyCardv2. Uses executeCommand, telegram, readBinaryFile, googleDrive. Webhook trigger; 41 nodes.
Settings. Uses httpRequest, postgres, telegram. Webhook trigger; 38 nodes.
[](https://www.linkedin.com/in/mosaab-yassir-lafrimi/)[](https://t.me/joevenner)