This workflow follows the Agent → Emailsend 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": "f1_bot_zoom",
"nodes": [
{
"parameters": {
"errorMessage": "={{ $json.error.cause.message }}"
},
"id": "9774e8ba-a253-483c-9418-d426235e2ea7",
"name": "No Recording/Transcript available",
"type": "n8n-nodes-base.stopAndError",
"position": [
-20,
180
],
"typeVersion": 1
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "ef149af8-7f9d-4e5a-8ccf-4a5f1e09eecc",
"name": "transcript_file",
"type": "string",
"value": "={{ $json.recording_files.find(f => f.file_type === 'CC' && f.file_extension === 'VTT')?.download_url }}"
}
]
},
"options": {}
},
"id": "733d944f-fb3b-4218-ac98-48f75c7bc55c",
"name": "Filter transcript URL",
"type": "n8n-nodes-base.set",
"position": [
-20,
0
],
"typeVersion": 3.4,
"onError": "continueRegularOutput"
},
{
"parameters": {
"url": "={{ $json.transcript_file }}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "zoomOAuth2Api",
"options": {}
},
"id": "4c1d9372-2c37-44cb-b8fa-4f40a5b2ebd2",
"name": "Zoom: Get transcript file",
"type": "n8n-nodes-base.httpRequest",
"position": [
160,
0
],
"typeVersion": 4.2,
"credentials": {
"zoomOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "text",
"options": {}
},
"id": "7171bd99-584b-492e-b827-b29366d07f74",
"name": "Extract text from transcript file",
"type": "n8n-nodes-base.extractFromFile",
"position": [
340,
0
],
"typeVersion": 1
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "70019192-02ef-4b0a-a747-3ca5f46aeeaa",
"name": "transcript",
"type": "string",
"value": "={{ $json.data.split('\\r\\n\\r\\n').slice(1).map(block => {\n const lines = block.split('\\r\\n');\n return lines.slice(2).join(' ');\n}).join('\\n') }}"
}
]
},
"options": {}
},
"id": "2861c0dd-37b4-441c-80f0-1936d05749e3",
"name": "Format transcript text",
"type": "n8n-nodes-base.set",
"position": [
500,
0
],
"typeVersion": 3.4
},
{
"parameters": {
"url": "=https://api.zoom.us/v2/past_meetings/{{ $('basic-info-depuration-I').item.json.meetingId }}/participants",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "zoomOAuth2Api",
"options": {}
},
"id": "4f6d7099-0db4-4a60-a953-c6e30bbb3277",
"name": "Zoom: Get participants data",
"type": "n8n-nodes-base.httpRequest",
"position": [
680,
0
],
"typeVersion": 4.2,
"credentials": {
"zoomOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "cc51b7e4-d5c2-4cd4-9488-4d181eaaa02e",
"name": "subject",
"type": "string",
"value": "=\ud83d\udce2 Resumen de la Clase: \"{{ $('Zoom: Get data by meetingId').item.json.topic }}\" - {{ DateTime.fromISO($('Zoom: Get data by meetingId').item.json.start_time).toFormat('yyyy-MM-dd HH:mm') }}"
},
{
"id": "f3940ea2-9084-4c25-828e-5ddaa428ec83",
"name": "=to",
"type": "string",
"value": "=hola@uai.edu.pe, mariana.campos@autonomadeica.edu.pe, dany.mejia@autonomadeica.edu.pe, humberto.quispe@autonomadeica.edu.pe, yassir.bonifacio@autonomadeica.edu.pe, susana.atuncar@autonomadeica.edu.pe, angemar@autonomadeica.edu.pe, karina.garcia@autonomadeica.edu.pe"
},
{
"id": "1211af5b-2240-44ce-9df7-63d93f57806e",
"name": "body",
"type": "string",
"value": "={{ $json.output }}"
}
]
},
"options": {}
},
"id": "f7403416-13c6-40dd-84e9-877f77c607e0",
"name": "Sort for mail delivery",
"type": "n8n-nodes-base.set",
"position": [
1280,
20
],
"typeVersion": 3.4
},
{
"parameters": {
"jsCode": "const items = [];\nfor (const item of $input.all()) {\n const body = item.json.body;\n if (!body) continue;\n \n const formatMarkdown = (text) => {\n return text\n .replace(/\\*\\*(.*?)\\*\\*/g, '<strong>$1</strong>') // Negrita\n .replace(/\\n/g, '<br>'); // Saltos de l\u00ednea\n };\n \n const getSection = (sectionTitle) => {\n const regex = new RegExp(`###\\\\s*${sectionTitle}\\\\s*\\\\n([\\\\s\\\\S]*?)(?=\\\\n###|\\\\n---|$)`, 'i');\n const match = body.match(regex);\n return match ? match[1].trim() : '';\n };\n \n const titleMatch = body.match(/\\*\\*\ud83d\udcc5 Fecha:\\*\\*\\s*(.*?)\\s\\s/);\n const title = titleMatch ? titleMatch[1].trim() : \"Acta de Reuni\u00f3n\";\n \n const resumen = getSection(\"\ud83d\udcda Resumen general de la clase\");\n const observacion = getSection(\"\ud83d\udd0e Observaci\u00f3n sobre la presencia y participaci\u00f3n del docente\");\n const citas = getSection(\"\ud83d\udcac Intervenciones destacadas o citas relevantes\");\n const conclusion = getSection(\"\ud83c\udf1f Sugerencias para la mejora docente\");\n \n const citasFormateadas = citas\n .split(\"\\n\")\n .filter(line => line.trim() !== \"\")\n .map(line => `<p style=\"margin: 8px 0; color: #2c3e50; font-style: italic; position: relative; padding-left: 20px;\">\"${line.trim()}\"</p>`)\n .join(\"\\n\");\n \n const conclusionFormateada = formatMarkdown(\n conclusion.replace(/^> /gm, '').trim()\n );\n \n const html = `\n<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Acta de Reuni\u00f3n - ${title}</title>\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n \n body {\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n min-height: 100vh;\n padding: 20px;\n color: #333;\n }\n \n .container {\n max-width: 800px;\n margin: 0 auto;\n background: white;\n border-radius: 20px;\n box-shadow: 0 20px 40px rgba(0,0,0,0.1);\n overflow: hidden;\n animation: slideIn 0.8s ease-out;\n }\n \n @keyframes slideIn {\n from { opacity: 0; transform: translateY(30px); }\n to { opacity: 1; transform: translateY(0); }\n }\n \n .header {\n background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);\n padding: 30px;\n text-align: center;\n color: white;\n position: relative;\n overflow: hidden;\n }\n \n .header::before {\n content: '';\n position: absolute;\n top: -50%;\n left: -50%;\n width: 200%;\n height: 200%;\n background: radial-gradient(circle, rgba(255,255,255,0.1) 1px, transparent 1px);\n background-size: 20px 20px;\n animation: float 20s linear infinite;\n }\n \n @keyframes float {\n 0% { transform: rotate(0deg) translateX(0px); }\n 100% { transform: rotate(360deg) translateX(10px); }\n }\n \n .logo {\n position: relative;\n z-index: 2;\n margin-bottom: 20px;\n }\n \n .logo img {\n width: 80px;\n height: 80px;\n width: auto;\n height: auto;\n border-radius: 10px;\n box-shadow: 0 4px 15px rgba(0,0,0,0.2);\n background: white;\n padding: 5px;\n animation: logoGlow 2s ease-in-out infinite alternate;\n }\n \n @keyframes logoGlow {\n from { box-shadow: 0 4px 15px rgba(0,0,0,0.2); }\n to { box-shadow: 0 4px 20px rgba(255,255,255,0.3); }\n }\n \n .header h1 {\n font-size: 28px;\n margin-bottom: 10px;\n position: relative;\n z-index: 1;\n }\n \n .header .date {\n font-size: 18px;\n opacity: 0.9;\n position: relative;\n z-index: 1;\n }\n \n .content {\n padding: 40px;\n }\n \n .section {\n margin-bottom: 35px;\n animation: fadeIn 0.6s ease-out;\n }\n \n @keyframes fadeIn {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n \n .section-title {\n font-size: 22px;\n color: #2c3e50;\n margin-bottom: 20px;\n padding-bottom: 10px;\n border-bottom: 3px solid #3498db;\n display: flex;\n align-items: center;\n gap: 10px;\n }\n \n .section-content {\n background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);\n padding: 25px;\n border-radius: 15px;\n border-left: 5px solid #3498db;\n line-height: 1.8;\n font-size: 16px;\n color: #2c3e50;\n position: relative;\n overflow: hidden;\n }\n \n .section-content::before {\n content: '';\n position: absolute;\n top: 0;\n left: -100%;\n width: 100%;\n height: 100%;\n background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);\n animation: shine 3s infinite;\n }\n \n @keyframes shine {\n 0% { left: -100%; }\n 100% { left: 100%; }\n }\n \n .citas-section {\n background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);\n color: #2c3e50;\n padding: 25px;\n border-radius: 15px;\n border-left: 5px solid #9b59b6;\n position: relative;\n overflow: hidden;\n }\n \n .citas-section::before {\n content: '';\n position: absolute;\n top: 0;\n left: -100%;\n width: 100%;\n height: 100%;\n background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);\n animation: shine 4s infinite;\n }\n \n .citas-section p {\n color: #2c3e50 !important;\n margin: 12px 0;\n font-size: 16px;\n position: relative;\n z-index: 1;\n }\n \n .citas-section p::before {\n content: '\ud83d\udcac';\n position: absolute;\n left: -20px;\n color: #9b59b6;\n }\n \n .conclusion-section {\n background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);\n color: #2c3e50;\n padding: 25px;\n border-radius: 15px;\n border-left: 5px solid #e74c3c;\n position: relative;\n overflow: hidden;\n }\n \n .conclusion-section::before {\n content: '';\n position: absolute;\n top: 0;\n left: -100%;\n width: 100%;\n height: 100%;\n background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);\n animation: shine 5s infinite;\n }\n \n .conclusion-section .section-content {\n background: transparent;\n border: none;\n padding: 0;\n color: #2c3e50;\n font-size: 16px;\n position: relative;\n z-index: 1;\n }\n \n .footer {\n background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);\n color: white;\n padding: 25px;\n text-align: center;\n font-size: 14px;\n }\n \n .icon {\n font-size: 24px;\n margin-right: 8px;\n }\n \n @media (max-width: 600px) {\n .container {\n margin: 10px;\n border-radius: 15px;\n }\n \n .header, .content {\n padding: 20px;\n }\n \n .header h1 {\n font-size: 24px;\n }\n \n .section-title {\n font-size: 20px;\n }\n \n .logo img {\n max-width: 100px;\n max-height: 70px;\n }\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <div class=\"logo\">\n <img src=\"https://drive.google.com/uc?id=1r2eXR7N8-MyJ0jyqGwosqjI_hXo6OABZ\" alt=\"Logo Tutor.IA\" onerror=\"this.style.display='none'\">\n </div>\n <h1>\ud83d\udccb Detalles de la Sesion</h1>\n <div class=\"date\">\ud83d\udcc5 ${title}</div>\n </div>\n \n <div class=\"content\">\n ${resumen ? `\n <div class=\"section\">\n <h2 class=\"section-title\">\n <span class=\"icon\">\ud83d\udcda</span>\n Resumen de la clase\n </h2>\n <div class=\"section-content\">\n ${formatMarkdown(resumen)}\n </div>\n </div>\n ` : \"\"}\n \n ${observacion ? `\n <div class=\"section\">\n <h2 class=\"section-title\">\n <span class=\"icon\">\ud83d\udd0e</span>\n Observaci\u00f3n sobre la presencia del docente\n </h2>\n <div class=\"section-content\">\n ${formatMarkdown(observacion)}\n </div>\n </div>\n ` : \"\"}\n \n ${citas ? `\n <div class=\"section\">\n <h2 class=\"section-title\">\n <span class=\"icon\">\ud83d\udcac</span>\n Citas destacadas\n </h2>\n <div class=\"citas-section\">\n ${citasFormateadas}\n </div>\n </div>\n ` : \"\"}\n \n ${conclusion ? `\n <div class=\"section\">\n <h2 class=\"section-title\">\n <span class=\"icon\">\ud83c\udf1f</span>\n Sugerencias para la mejora docente\n </h2>\n <div class=\"conclusion-section\">\n <div class=\"section-content\">\n ${conclusionFormateada}\n </div>\n </div>\n </div>\n ` : \"\"}\n </div>\n \n <div class=\"footer\">\n <p>\u00a9 2025 Tutor.IA - Acta generada autom\u00e1ticamente</p>\n </div>\n </div>\n</body>\n</html>`;\n\n items.push({\n json: {\n html,\n to: item.json.to,\n subject: item.json.subject,\n },\n });\n}\n\nreturn items;"
},
"id": "b2461809-3b0d-4bad-b030-e01c6110f600",
"name": "Format to html",
"type": "n8n-nodes-base.code",
"position": [
1560,
0
],
"typeVersion": 2
},
{
"parameters": {
"fromEmail": "tutor.ia@uai.edu.pe",
"toEmail": "={{ $json.to }}",
"subject": "={{ $json.subject }}",
"html": "={{ $json.html }}",
"options": {}
},
"id": "86c03aa4-b797-469a-8c7a-56504c0cf0ff",
"name": "Send meeting summary",
"type": "n8n-nodes-base.emailSend",
"position": [
1760,
0
],
"typeVersion": 2.1,
"credentials": {
"smtp": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "=GET",
"url": "=https://api.zoom.us/v2/meetings/{{ $json.id }}/recordings",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "zoomOAuth2Api",
"options": {}
},
"id": "079c3aa5-cb81-4172-8ab1-f23dd76c2ef5",
"name": "Zoom: Get transcripts data",
"type": "n8n-nodes-base.httpRequest",
"position": [
-220,
20
],
"typeVersion": 4.2,
"credentials": {
"zoomOAuth2Api": {
"name": "<your credential>"
}
},
"onError": "continueErrorOutput"
},
{
"parameters": {
"promptType": "define",
"text": "=Act\u00faa como un supervisor acad\u00e9mico que eval\u00faa de forma objetiva, clara y constructiva la calidad de una clase grabada en Zoom. Tu tarea es redactar un acta formal que resuma el desarrollo de la sesi\u00f3n, identifique puntos de mejora realistas y valore las oportunidades de crecimiento docente. Mant\u00e9n un tono profesional, imparcial y emp\u00e1tico: no sanciones, orienta.\n\nUtiliza los siguientes datos:\n\n- **Fecha programada de la clase:** {{ $('basic-info-depuration-II').item.json.startTimeLocal }} \n- **Hora en la que el host inici\u00f3 la reuni\u00f3n:** {{ $('basic-info-depuration-I').item.json.startTimeLocal }} \n- **Docente (host):** {{ $('Zoom: Get transcripts data').item.json.host_email }}\n\n---\n\n**Transcript crudo con tiempo** (\u00fatil para an\u00e1lisis de presencia y participaci\u00f3n): \n{{ $('Extract text from transcript file').item.json.data }}\n\n**Transcript limpio** (\u00fatil para redacci\u00f3n del acta): \n{{ $('Format transcript text').item.json.transcript }}\n\n---\n\nRedacta el acta con la siguiente estructura:\n\n---\n\n### \ud83d\udcdd Acta de Reuni\u00f3n\n\n**\ud83d\udcc5 Fecha:** [En formato DD/MM/AAAA] \n**\ud83d\udd50 Hora programada:** [Hora programada de la clase] \n**\ud83d\udd50 Hora real de inicio del host:** [Hora registrada de inicio de la reuni\u00f3n] \n\n---\n\n### \ud83d\udcda Resumen general de la clase \nRedacta un p\u00e1rrafo **breve** que describa el enfoque tem\u00e1tico (si lo hay), nivel de preparaci\u00f3n, claridad de objetivos, estructura y participaci\u00f3n. Menciona aspectos positivos o destacables, pero tambi\u00e9n debilidades o \u00e1reas de mejora, usando **negrita** para resaltar hallazgos clave. Mant\u00e9n un tono constructivo y profesional.\n\n---\n\n### \ud83d\udd0e Observaci\u00f3n sobre la presencia y participaci\u00f3n del docente \n1. Especifica si el docente inici\u00f3 la reuni\u00f3n antes, a tiempo o despu\u00e9s de la hora programada. \n2. Indica el **momento exacto en el transcript** (en segundos o formato `00:MM:SS`) en el que el docente habl\u00f3 por primera vez. \n3. Eval\u00faa si hubo **dilataci\u00f3n excesiva entre intervenciones** (por ejemplo, si hay m\u00e1s de 20 minutos sin participaci\u00f3n del docente). Si ocurre, dest\u00e1calo como una se\u00f1al de posible desconexi\u00f3n o falta de conducci\u00f3n activa. \n4. Usa un tono objetivo y orientado a la mejora profesional.\n\n---\n\n### \ud83d\udcac Intervenciones destacadas o citas relevantes \nSelecciona m\u00e1ximo 6 l\u00edneas directamente copiadas textualmente del transcript crudo con timestamp incluido, sin reformular ni parafrasear. \nCada l\u00ednea debe mantenerse **id\u00e9ntica** a como aparece en la transcripci\u00f3n, incluyendo comillas, puntuaci\u00f3n, pausas u onomatopeyas. \nPresenta cada cita como un \u00edtem de lista con vi\u00f1eta tipo guion (`-`), en este formato:\n\n- \"Texto exacto de la intervenci\u00f3n.\" (`00:00:00`)\n\n\u2757 No agregues introducciones, no resumas ni cambies la redacci\u00f3n. Mant\u00e9n el estilo literal.\n\n---\n\n### \ud83c\udf1f Sugerencias para la mejora docente \nBrinda **solo 2 recomendaciones** puntuales para mejorar el desempe\u00f1o docente, usando emojis y tono motivador. \nEjemplos:\n\n- \ud83d\udccc Establecer un objetivo claro y compartido al inicio \n- \ud83e\uddd1\u200d\ud83c\udfeb Asegurar una presencia activa y constante durante la clase \n- \ud83e\uddf0 Incorporar ejemplos o recursos visuales que refuercen el contenido \n- \ud83c\udfaf Guiar la participaci\u00f3n de los estudiantes con preguntas relevantes \n- \ud83d\uddc2\ufe0f Estructurar la sesi\u00f3n en secciones claras: introducci\u00f3n, desarrollo y cierre \n- \u23f1\ufe0f Controlar mejor el tiempo para mantener el ritmo y cubrir todos los puntos clave\n\nFinaliza con un mensaje positivo:\n\n> \u2728 Cada clase ofrece una oportunidad para mejorar. Con peque\u00f1os ajustes en la preparaci\u00f3n y conducci\u00f3n, esta experiencia puede transformarse en una sesi\u00f3n significativa para los estudiantes. \u00a1Adelante!\n\n---\n\n### \u2705 Instrucciones finales \n- S\u00e9 profesional, preciso y respetuoso. \n- Usa **negrita** para resaltar los hallazgos clave. \n- No inventes datos si no est\u00e1n disponibles. \n- Evita repetir ideas y mant\u00e9n el informe claro y enfocado.\n",
"options": {}
},
"id": "ad3987fd-42ae-4f2b-837d-7dbc2f36c72c",
"name": "Create meeting summary",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
860,
0
],
"typeVersion": 1.9
},
{
"parameters": {
"httpMethod": "POST",
"path": "recording-completed",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-980,
20
],
"id": "4f38263b-231e-4def-84b4-c7ecf83b839e",
"name": "recording-completed"
},
{
"parameters": {
"authentication": "oAuth2",
"operation": "get",
"meetingId": "={{ $json.meetingId }}",
"additionalFields": {}
},
"id": "d40664f1-eaa6-4057-bdfe-d8d596073e78",
"name": "Zoom: Get data by meetingId",
"type": "n8n-nodes-base.zoom",
"position": [
-600,
20
],
"typeVersion": 1,
"alwaysOutputData": false,
"executeOnce": false,
"retryOnFail": false,
"credentials": {
"zoomOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
840,
220
],
"id": "0d845e89-3f7b-4f4d-a479-95b601209810",
"name": "OpenAI Chat Model",
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const { object } = $json.body.payload;\n\n// Convertir start_time UTC a hora local (Per\u00fa)\nconst startTimeUTC = new Date(object.start_time);\n\nconst options = {\n timeZone: 'America/Lima',\n hour12: false,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n};\n\nconst startTimeLocal = startTimeUTC.toLocaleString('es-PE', options); // ejemplo: \"27/06/2025, 10:30\"\n\nreturn {\n json: {\n meetingId: object.id,\n topic: object.topic,\n startTimeUTC: object.start_time,\n startTimeLocal: startTimeLocal,\n uuid: object.uuid,\n hostId: object.host_id\n }\n};\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-780,
20
],
"id": "bde1b6d0-c87e-4c50-bb80-10c5bc7b4c7f",
"name": "basic-info-depuration-I"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const data = $json;\n\n// Convertir start_time de UTC a hora local (Per\u00fa)\nconst startTimeUTC = new Date(data.start_time);\nconst options = {\n timeZone: 'America/Lima',\n hour12: false,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n};\nconst startTimeLocal = startTimeUTC.toLocaleString('es-PE', options); // Resultado: \"27/06/2025, 10:30\"\n\nreturn {\n json: {\n uuid: data.uuid,\n id: data.id,\n hostId: data.host_id,\n hostEmail: data.host_email,\n assistantId: data.assistant_id,\n topic: data.topic,\n type: data.type,\n status: data.status,\n startTimeUTC: data.start_time,\n startTimeLocal: startTimeLocal,\n duration: data.duration,\n timezone: data.timezone,\n agenda: data.agenda,\n createdAt: data.created_at,\n startUrl: data.start_url,\n joinUrl: data.join_url\n }\n};\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-420,
20
],
"id": "41b4e133-7387-43f9-8512-eb790b761d07",
"name": "basic-info-depuration-II"
},
{
"parameters": {
"httpMethod": "POST",
"path": "meeting-started",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-1000,
-580
],
"id": "47e49a81-ccce-47b3-b053-4f906af79d6c",
"name": "meeting-started"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "cc51b7e4-d5c2-4cd4-9488-4d181eaaa02e",
"name": "subject",
"type": "string",
"value": "=\ud83d\udcc4 Formulario de la clase: \"{{ $('meeting-info').item.json.meeting.topic }}\" - {{ $('meeting-info').item.json.meeting.startTime }}"
},
{
"id": "f3940ea2-9084-4c25-828e-5ddaa428ec83",
"name": "=to",
"type": "string",
"value": "={{ $('meeting-info').item.json.meeting.host.email }}"
},
{
"id": "1211af5b-2240-44ce-9df7-63d93f57806e",
"name": "body",
"type": "string",
"value": "=<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Tutor.IA - Mensaje</title>\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n \n body {\n font-family: 'Arial', sans-serif;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n min-height: 100vh;\n padding: 20px;\n }\n \n .container {\n max-width: 600px;\n margin: 0 auto;\n background: white;\n border-radius: 20px;\n box-shadow: 0 20px 40px rgba(0,0,0,0.1);\n overflow: hidden;\n animation: slideIn 0.8s ease-out;\n }\n \n @keyframes slideIn {\n from {\n opacity: 0;\n transform: translateY(30px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n \n .header {\n background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);\n padding: 30px;\n text-align: center;\n position: relative;\n overflow: hidden;\n }\n \n .header::before {\n content: '';\n position: absolute;\n top: -50%;\n left: -50%;\n width: 200%;\n height: 200%;\n background: radial-gradient(circle, rgba(255,255,255,0.1) 1px, transparent 1px);\n background-size: 20px 20px;\n animation: float 20s linear infinite;\n }\n \n @keyframes float {\n 0% { transform: rotate(0deg) translateX(0px); }\n 100% { transform: rotate(360deg) translateX(10px); }\n }\n \n .logo {\n width: 455px;\n height: 120px;\n margin: 0 auto 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 1;\n animation: pulse 2s infinite;\n overflow: hidden;\n border-radius: 15px;\n }\n \n .logo img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n border-radius: 15px;\n }\n \n @keyframes pulse {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n }\n \n .company-name {\n color: white;\n font-size: 28px;\n font-weight: bold;\n position: relative;\n z-index: 1;\n }\n \n .content {\n padding: 40px;\n }\n \n .greeting {\n font-size: 24px;\n color: #2d3748;\n margin-bottom: 30px;\n font-weight: 600;\n }\n \n .message {\n font-size: 16px;\n line-height: 1.8;\n color: #4a5568;\n margin-bottom: 30px;\n }\n \n .link-section {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n padding: 25px;\n border-radius: 15px;\n margin: 30px 0;\n text-align: center;\n position: relative;\n overflow: hidden;\n }\n \n .link-section::before {\n content: '';\n position: absolute;\n top: 0;\n left: -100%;\n width: 100%;\n height: 100%;\n background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);\n animation: shine 3s infinite;\n }\n \n @keyframes shine {\n 0% { left: -100%; }\n 100% { left: 100%; }\n }\n \n .link-section h3 {\n margin-bottom: 15px;\n position: relative;\n z-index: 1;\n }\n \n .link-section p {\n position: relative;\n z-index: 1;\n }\n \n .link-section a {\n color: #ffd700;\n text-decoration: none;\n font-weight: bold;\n word-break: break-all;\n }\n \n .link-section a:hover {\n text-decoration: underline;\n }\n \n .footer {\n background: linear-gradient(135deg, #2d3748 0%, #4a5568 100%);\n color: white;\n padding: 30px;\n text-align: center;\n }\n \n .contact-info {\n font-size: 14px;\n color: rgba(255,255,255,0.8);\n }\n \n @media (max-width: 600px) {\n .container {\n margin: 10px;\n border-radius: 15px;\n }\n \n .header, .content, .footer {\n padding: 20px;\n }\n \n .logo {\n width: 290px;\n height: 100px;\n }\n \n .company-name {\n font-size: 24px;\n }\n \n .greeting {\n font-size: 20px;\n }\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <div class=\"logo\">\n <img src=\"https://drive.google.com/uc?id=1l22pb0dFRDVHnOs4ijKMQZlXI8hy5peG\" alt=\"Tutor.IA Logo\">\n </div>\n </div>\n <div class=\"content\">\n <div class=\"greeting\">\u00a1Hola, {{ $('meeting-info').item.json.meeting.host.email }}! \ud83d\ude04</div>\n <div class=\"message\">Se le comparte el link para que sus alumnos le den feedback a su clase.</div>\n <div class=\"link-section\">\n <p><a href=\"{{ $json.formLink }}\" target=\"_blank\">{{ $json.formLink }}</a></p>\n </div>\n </div>\n <div class=\"footer\">\n <div class=\"contact-info\">\n <small>\u00a9 2025 Tutor.IA. Todos los derechos reservados.</small>\n </div>\n </div>\n </div>\n</body>"
},
{
"id": "de580983-c982-46c8-94c2-395580b1bee7",
"name": "",
"value": "",
"type": "string"
}
]
},
"options": {}
},
"id": "d170faf4-3b11-4d05-92d3-003ab95d977d",
"name": "Sort for mail",
"type": "n8n-nodes-base.set",
"position": [
-200,
-580
],
"typeVersion": 3.4
},
{
"parameters": {
"fromEmail": "tutor.ia@uai.edu.pe",
"toEmail": "={{ $json.to }}",
"subject": "={{ $json.subject }}",
"html": "={{ $json.body }}",
"options": {}
},
"id": "9eeecca8-f22d-4909-aaf8-7d4ffcc740b2",
"name": "Send url for students",
"type": "n8n-nodes-base.emailSend",
"position": [
0,
-580
],
"typeVersion": 2.1,
"credentials": {
"smtp": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"authentication": "oAuth2",
"operation": "get",
"meetingId": "={{ $json.body.payload.object.id }}",
"additionalFields": {}
},
"id": "73e7e906-9d57-4d0f-bd0d-f150c918ff9b",
"name": "Zoom: Get-data-by-meetingId",
"type": "n8n-nodes-base.zoom",
"position": [
-820,
-580
],
"typeVersion": 1,
"alwaysOutputData": false,
"executeOnce": false,
"retryOnFail": false,
"credentials": {
"zoomOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const object = $json;\n\n// Convertir start_time UTC a hora local (Per\u00fa)\nconst startTimeUTC = new Date(object.start_time);\n\nconst options = {\n timeZone: \"America/Lima\",\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n};\n\nconst startTimeLocal = startTimeUTC.toLocaleString(\"es-PE\", options); // ejemplo: \"27/06/2025, 10:30\"\n\nreturn {\n json: {\n meeting: {\n id: object.id,\n uuid: object.uuid,\n topic: object.topic,\n startTime: startTimeLocal,\n status: object.status,\n duration: object.duration,\n host: {\n id: object.host_id,\n email: object.host_email,\n },\n }\n },\n};\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-620,
-580
],
"id": "a3c44fdf-3f9d-4ecd-85f6-07648d9992ca",
"name": "meeting-info"
},
{
"parameters": {
"httpMethod": "POST",
"path": "participant-joined",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-980,
-180
],
"id": "f29afb03-db1f-4301-b5ef-1b18687eb0a3",
"name": "participants-joined"
},
{
"parameters": {
"method": "POST",
"url": "https://known-moth-included.ngrok-free.app/ms/v1/meeting/save",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"payload\": {\n \"object\": {\n \"id\": {{ $json.meeting.id }},\n \"uuid\": \"{{ $json.meeting.uuid }}\",\n \"topic\": \"{{ $json.meeting.topic }}\",\n \"start_time\": \"{{ $json.meeting.startTime }}\",\n \"join_url\": \"{{ $json.meeting.join_url }}\",\n \"status\": \"pending\",\n \"duration\": {{ $json.meeting.duration }},\n \"host_id\": \"{{ $json.meeting.host.id }}\",\n \"host_email\": \"{{ $json.meeting.host.email }}\"\n }\n }\n}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-560,
-800
],
"id": "9cce9439-8e84-44e5-8d52-9c896eb59866",
"name": "/ms/v1/meeting/save"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const eventObject = $json.body.payload.object;\nconst participant = eventObject.participant;\n\n// Convertir start_time UTC a hora local (Per\u00fa)\nconst joinTimeUTC = new Date(participant.join_time);\n\nconst options = {\n timeZone: \"America/Lima\",\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n};\n\nconst joinTimeLocal = joinTimeUTC.toLocaleString(\"es-PE\", options);\n\nreturn {\n json: {\n meeting: {\n id: eventObject.id,\n uuid: eventObject.uuid,\n },\n participant: {\n id: participant.id,\n user_id: participant.user_id,\n participant_user_id: participant.participant_user_id || null,\n user_name: participant.user_name,\n email: participant.email || null,\n join_time: joinTimeLocal,\n isHost: participant.participant_user_id === eventObject.host_id,\n },\n host_id: eventObject.host_id,\n },\n};\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-780,
-180
],
"id": "527929ac-6634-431c-a7d4-f09047d19e68",
"name": "participant-info"
},
{
"parameters": {
"method": "POST",
"url": "https://known-moth-included.ngrok-free.app/ms/v1/participant/save",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"payload\": {\n \"object\": {\n \"user_id\": \"{{ $json.participant.user_id }}\",\n \"participant_user_id\": \"{{ $json.participant.participant_user_id }}\",\n \"user_name\": \"{{ $json.participant.user_name }}\",\n \"email\": \"{{ $json.participant.email }}\",\n \"join_time\": \"{{ $json.participant.join_time }}\",\n \"isHost\": {{ $json.participant.isHost }},\n \"host_id\": \"{{ $json.host_id }}\",\n \"meeting_id\": \"{{ $json.meeting.id }}\",\n \"meeting_uuid\": \"{{ $json.meeting.uuid }}\"\n }\n }\n}\n\n",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-560,
-180
],
"id": "a8209758-1070-4f59-b68e-4bf6607efcba",
"name": "/ms/v1/participant/save"
},
{
"parameters": {
"authentication": "oAuth2",
"operation": "getAll",
"returnAll": true,
"filters": {}
},
"type": "n8n-nodes-base.zoom",
"typeVersion": 1,
"position": [
920,
980
],
"id": "4fce3783-4f7d-48fb-95cc-cd13766dc297",
"name": "Get many meetings",
"credentials": {
"zoomOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const uniqueMeetingsMap = new Map();\n\nitems\n .sort((a, b) => new Date(b.json.created_at) - new Date(a.json.created_at))\n .forEach(item => {\n const now = new Date();\n const start = new Date(item.json.start_time);\n const end = new Date(start.getTime() + item.json.duration * 60000);\n\n let estado = '';\n if (now < start) {\n estado = 'pendiente';\n } else if (now >= start && now <= end) {\n estado = 'en curso';\n } else {\n estado = 'finalizado';\n }\n\n // Solo agrega si a\u00fan no existe una reuni\u00f3n con este ID\n if (!uniqueMeetingsMap.has(item.json.id)) {\n uniqueMeetingsMap.set(item.json.id, {\n json: {\n ...item.json,\n estado,\n }\n });\n }\n });\n\n// Tomar los primeros 15 elementos \u00fanicos\nreturn Array.from(uniqueMeetingsMap.values()).slice(0, 15);\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1140,
980
],
"id": "6f55cee4-2bdd-4b89-97f2-50726eef417e",
"name": "add status field"
},
{
"parameters": {
"path": "/api/reuniones/get-all",
"responseMode": "responseNode",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
720,
980
],
"id": "0e658ff2-29a3-4b92-8a75-f9d61b0dc657",
"name": "api/reuniones",
"disabled": true
},
{
"parameters": {
"respondWith": "allIncomingItems",
"options": {
"responseCode": 200
}
},
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.4,
"position": [
1360,
980
],
"id": "df02834a-99bc-42b0-b6d7-b561b4d6c34b",
"name": "endpoint"
},
{
"parameters": {
"jsCode": "const uriBase = \"https://encuesta-e0vth.web.app\"\nconst idMeeting = $input.first().json.meeting.id\nconst idHost = $input.first().json.meeting.host.id\n\nreturn {\n formLink: `${uriBase}/${idMeeting}/${idHost}`\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-420,
-580
],
"id": "60cacd45-4125-4116-bd75-0c5c99eaa84e",
"name": "encuesta - link"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "cc51b7e4-d5c2-4cd4-9488-4d181eaaa02e",
"name": "subject",
"type": "string",
"value": "=\ud83d\udce2 Resumen de la Clase: \"{{ $('Zoom: Get data by meetingId').item.json.topic }}\" - {{ DateTime.fromISO($('Zoom: Get data by meetingId').item.json.start_time).toFormat('yyyy-MM-dd HH:mm') }}"
},
{
"id": "f3940ea2-9084-4c25-828e-5ddaa428ec83",
"name": "=to",
"type": "string",
"value": "=hola@uai.edu.pe"
},
{
"id": "1211af5b-2240-44ce-9df7-63d93f57806e",
"name": "body",
"type": "string",
"value": "={{ $json.output }}"
}
]
},
"options": {}
},
"id": "b0965436-1479-45e3-bf40-701d300982bf",
"name": "TEST - Sort for mail delivery",
"type": "n8n-nodes-base.set",
"position": [
1280,
220
],
"typeVersion": 3.4
},
{
"parameters": {
"httpMethod": "POST",
"path": "meeting-created",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-1000,
-800
],
"id": "18d78d06-8cc3-45b7-967d-f80b52c7ff8e",
"name": "meeting-created"
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const meeting = $json.body.payload.object;\nconst operatorEmail = $json.body.payload.operator;\n\n// Convertir start_time UTC a hora local (Per\u00fa)\nconst startTimeUTC = new Date(meeting.start_time);\n\nconst options = {\n timeZone: \"America/Lima\",\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n};\n\nconst startTimeLocal = startTimeUTC.toLocaleString(\"es-PE\", options);\n\nreturn {\n json: {\n meeting: {\n id: meeting.id,\n uuid: meeting.uuid,\n topic: meeting.topic,\n startTime: startTimeLocal,\n join_url: meeting.join_url,\n status: \"pending\",\n duration: meeting.duration,\n host: {\n id: meeting.host_id,\n email: operatorEmail || \"\",\n },\n },\n },\n};\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-760,
-800
],
"id": "1df42446-7f3c-4781-b962-c03fc40077e5",
"name": "meeting-info1"
},
{
"parameters": {
"method": "PATCH",
"url": "=https://known-moth-included.ngrok-free.app/ms/v1/meeting/status/{{ $json.meeting.id }}",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"status\": \"started\"\n}\n\n",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-420,
-400
],
"id": "ecd06f2d-df90-4b59-beb0-ed7e3544c42d",
"name": "/ms/v1/meeting/status/:meeting_id"
},
{
"parameters": {
"method": "PATCH",
"url": "=https://known-moth-included.ngrok-free.app/ms/v1/meeting/summary/{{ $('basic-info-depuration-I').item.json.meetingId }}",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"summary\": {{ $json.summary }}\n}\n\n",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1980,
220
],
"id": "f80e7608-2b29-498d-b85e-41f2d9cd7006",
"name": "/ms/v1/meeting/summary/:meeting_id"
},
{
"parameters": {
"jsCode": "const rawHtml = $json.html;\nreturn {\n summary: JSON.stringify(rawHtml) // convierte a string escapado v\u00e1lido\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1760,
220
],
"id": "ed481668-4674-4036-9f16-3de69b643efe",
"name": "format to firebase"
},
{
"parameters": {
"method": "PATCH",
"url": "=https://known-moth-included.ngrok-free.app/ms/v1/meeting/status/{{ $json.body.payload.object.id }}",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"status\": \"finished\"\n}\n\n",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-780,
300
],
"id": "0d762604-beb8-4dbe-95e9-7de8f3ff2101",
"name": "/ms/v1/meeting/status/:meeting_id - finished"
},
{
"parameters": {
"httpMethod": "POST",
"path": "meeting-end",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-1000,
300
],
"id": "2d9084d8-dd3c-4e8b-a49b-09c84b8634fa",
"name": "meeting-end"
},
{
"parameters": {
"content": "## CASI OBSOLETO\n",
"height": 260,
"width": 920
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
660,
920
],
"id": "0d5a8e52-9043-4653-b7a1-930a89c208b8",
"name": "Sticky Note"
}
],
"connections": {
"Format to html": {
"main": [
[
{
"node": "Send meeting summary",
"type": "main",
"index": 0
},
{
"node": "format to firebase",
"type": "main",
"index": 0
}
]
]
},
"Filter transcript URL": {
"main": [
[
{
"node": "Zoom: Get transcript file",
"type": "main",
"index": 0
}
]
]
},
"Create meeting summary": {
"main": [
[
{
"node": "TEST - Sort for mail delivery",
"type": "main",
"index": 0
}
]
]
},
"Format transcript text": {
"main": [
[
{
"node": "Zoom: Get participants data",
"type": "main",
"index": 0
}
]
]
},
"Zoom: Get transcript file": {
"main": [
[
{
"node": "Extract text from transcript file",
"type": "main",
"index": 0
}
]
]
},
"Zoom: Get transcripts data": {
"main": [
[
{
"node": "Filter transcript URL",
"type": "main",
"index": 0
}
],
[
{
"node": "No Recording/Transcript available",
"type": "main",
"index": 0
}
]
]
},
"Zoom: Get participants data": {
"main": [
[
{
"node": "Create meeting summary",
"type": "main",
"index": 0
}
]
]
},
"Extract text from transcript file": {
"main": [
[
{
"node": "Format transcript text",
"type": "main",
"index": 0
}
]
]
},
"Zoom: Get data by meetingId": {
"main": [
[
{
"node": "basic-info-depuration-II",
"type": "main",
"index": 0
}
]
]
},
"recording-completed": {
"main": [
[
{
"node": "basic-info-depuration-I",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "Create meeting summary",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"basic-info-depuration-I": {
"main": [
[
{
"node": "Zoom: Get data by meetingId",
"type": "main",
"index": 0
}
]
]
},
"basic-info-depuration-II": {
"main": [
[
{
"node": "Zoom: Get transcripts data",
"type": "main",
"index": 0
}
]
]
},
"meeting-started": {
"main": [
[
{
"node": "Zoom: Get-data-by-meetingId",
"type": "main",
"index": 0
}
]
]
},
"Sort for mail": {
"main": [
[
{
"node": "Send url for students",
"type": "main",
"index": 0
}
]
]
},
"Zoom: Get-data-by-meetingId": {
"main": [
[
{
"node": "meeting-info",
"type": "main",
"index": 0
}
]
]
},
"meeting-info": {
"main": [
[
{
"node": "encuesta - link",
"type": "main",
"index": 0
},
{
"node": "/ms/v1/meeting/status/:meeting_id",
"type": "main",
"index": 0
}
]
]
},
"participants-joined": {
"main": [
[
{
"node": "participant-info",
"type": "main",
"index": 0
}
]
]
},
"participant-info": {
"main": [
[
{
"node": "/ms/v1/participant/save",
"type": "main",
"index": 0
}
]
]
},
"Get many meetings": {
"main": [
[
{
"node": "add status field",
"type": "main",
"index": 0
}
]
]
},
"add status field": {
"main": [
[
{
"node": "endpoint",
"type": "main",
"index": 0
}
]
]
},
"api/reuniones": {
"main": [
[
{
"node": "Get many meetings",
"type": "main",
"index": 0
}
]
]
},
"encuesta - link": {
"main": [
[
{
"node": "Sort for mail",
"type": "main",
"index": 0
}
]
]
},
"TEST - Sort for mail delivery": {
"main": [
[
{
"node": "Format to html",
"type": "main",
"index": 0
}
]
]
},
"meeting-created": {
"main": [
[
{
"node": "meeting-info1",
"type": "main",
"index": 0
}
]
]
},
"meeting-info1": {
"main": [
[
{
"node": "/ms/v1/meeting/save",
"type": "main",
"index": 0
}
]
]
},
"/ms/v1/meeting/summary/:meeting_id": {
"main": [
[]
]
},
"format to firebase": {
"main": [
[
{
"node": "/ms/v1/meeting/summary/:meeting_id",
"type": "main",
"index": 0
}
]
]
},
"meeting-end": {
"main": [
[
{
"node": "/ms/v1/meeting/status/:meeting_id - finished",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"versionId": "3c5f0e01-ee16-4428-8724-a32d80d24d37",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "JA4YirAEmOgYdeDJ",
"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.
openAiApismtpzoomOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
f1_bot_zoom. Uses stopAndError, httpRequest, emailSend, agent. Webhook trigger; 39 nodes.
Source: https://github.com/holaUAI/n8n_workflows/blob/d23b7eccaf5b1a57257865deb93916541b033fd7/workflows/f1_bot_zoom.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.
Zoom AI Meeting Assistant. Uses lmChatOpenAi, manualTrigger, stopAndError, zoom. Event-driven trigger; 24 nodes.
This n8n workflow orchestrates a powerful suite of AI Agents and automations to manage and optimize various aspects of an e-commerce operation, particularly for platforms like Shopify. It leverages La
|Overview |Sample| |-|-| |This template is the first of its kind: it automatically generates both the caption and the image for your Instagram posts by analysing your existing feed, with zero spreadsh
This workflow transforms natural language queries into research reports through a five-stage AI pipeline. When triggered via webhook (typically from Google Sheets using the companion [](https://gist.g
This workflow is designed for professionals, companies, and agencies that want to automate competitive analysis based on public reviews and opinions found on the Internet. It is especially useful for: