AutomationFlowsAI & RAG › F1 Bot Zoom

F1 Bot Zoom

f1_bot_zoom. Uses stopAndError, httpRequest, emailSend, agent. Webhook trigger; 39 nodes.

Webhook trigger★★★★★ complexityAI-powered39 nodesStop And ErrorHTTP RequestEmail SendAgentZoomOpenAI Chat
AI & RAG Trigger: Webhook Nodes: 39 Complexity: ★★★★★ AI nodes: yes Added:

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 →

Download .json
{
  "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.

Pro

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 →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Zoom AI Meeting Assistant. Uses lmChatOpenAi, manualTrigger, stopAndError, zoom. Event-driven trigger; 24 nodes.

OpenAI Chat, Stop And Error, Zoom +8
AI & RAG

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

Google Sheets, HTTP Request, Slack +10
AI & RAG

|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

Airtable, HTTP Request, Agent +4
AI & RAG

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

Redis, Agent, Output Parser Structured +7
AI & RAG

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:

Agent, OpenAI Chat, OpenAI +4