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": "1 - Emision y Autorizacion en Tiempo Real",
"nodes": [
{
"parameters": {
"url": "={{ $('Preparar Respuesta Final').item.json.urlRide }}",
"options": {
"response": {
"response": {
"responseFormat": "file",
"outputPropertyName": "rideData"
}
}
}
},
"id": "7f2a18f7-017f-420b-84d7-297eb9d266f3",
"name": "Descargar RIDE (PDF)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
320,
-496
],
"executeOnce": true,
"onError": "continueRegularOutput"
},
{
"parameters": {
"url": "={{ $('Preparar Respuesta Final').item.json.urlXml }}",
"options": {
"response": {
"response": {
"responseFormat": "file",
"outputPropertyName": "xmlData"
}
}
}
},
"id": "19ac47f6-f468-482f-8fac-fc1998f56762",
"name": "Descargar XML",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
480,
-496
],
"executeOnce": false,
"onError": "continueRegularOutput"
},
{
"parameters": {
"inputDataFieldName": "rideData",
"name": "={{ $json.claveAcceso + '_RIDE.pdf' }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "1mN1YwRk9Qdgh_V5P0TB3XYh9VGu3xUws",
"mode": "id"
},
"options": {}
},
"id": "8e0e466a-9bed-4320-ba60-c1523713e6b4",
"name": "Respaldar RIDE en Drive",
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
560,
-272
],
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"inputDataFieldName": "xmlData",
"name": "={{ $json.claveAcceso + '_XML.xml' }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "1mN1YwRk9Qdgh_V5P0TB3XYh9VGu3xUws",
"mode": "id"
},
"options": {}
},
"id": "9fe64a78-e8c0-4cb9-bd17-958be6056d92",
"name": "Respaldar XML en Drive",
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
720,
-272
],
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"httpMethod": "POST",
"path": "facturar",
"responseMode": "responseNode",
"options": {}
},
"id": "13c11157-49d1-46e4-85ae-21ebd7ea3dd1",
"name": "Webhook Recibir Factura",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-1376,
-512
]
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nconst factura = items[0].json.body ?? items[0].json;\n\n// ============================================================\n// MANEJO DEL LOGO\n// ============================================================\n// Conservamos el Data URL completo:\n// data:image/png;base64,XXXX\n// data:image/jpeg;base64,XXXX\n// data:image/webp;base64,XXXX\n//\n// Esto permite que el server.js detecte correctamente el formato.\n// ============================================================\n\nif (!factura.emisor) {\n factura.emisor = {};\n}\n\nif (factura.emisor.logo) {\n const logoStr = String(factura.emisor.logo).trim();\n\n // NO eliminamos \"data:image/...;base64,\"\n // Se conserva el Data URL completo.\n factura.emisor.logo = logoStr;\n}\n\n// ============================================================\n// FECHA DE EMISI\u00d3N\n// ============================================================\n\nconst fecha = new Date(\n factura.fechaEmision || Date.now()\n);\n\nconst dd = String(\n fecha.getDate()\n).padStart(2, '0');\n\nconst mm = String(\n fecha.getMonth() + 1\n).padStart(2, '0');\n\nconst yyyy = String(\n fecha.getFullYear()\n);\n\nconst fechaEmisionStr =\n `${dd}${mm}${yyyy}`;\n\n// ============================================================\n// DATOS DEL COMPROBANTE\n// ============================================================\n\nconst tipoComprobante = '01';\n\nconst ruc = String(\n factura.emisor.ruc ||\n '1792146739001'\n)\n .padStart(13, '0')\n .slice(0, 13);\n\nconst ambiente = '1';\n\nconst establecimiento = String(\n factura.emisor.establecimiento ||\n '001'\n)\n .padStart(3, '0');\n\nconst puntoEmision = String(\n factura.emisor.puntoEmision ||\n '001'\n)\n .padStart(3, '0');\n\n// ============================================================\n// SECUENCIAL\n// ============================================================\n\nconst staticData =\n $getWorkflowStaticData('global');\n\nstaticData.secuencial =\n (staticData.secuencial || 0) + 1;\n\nconst secuencial = String(\n staticData.secuencial\n).padStart(9, '0');\n\n// ============================================================\n// C\u00d3DIGO NUM\u00c9RICO\n// ============================================================\n\nconst codigoNumerico = String(\n Math.floor(\n Math.random() * 100000000\n )\n).padStart(8, '0');\n\nconst tipoEmision = '1';\n\n// ============================================================\n// GENERAR CLAVE DE ACCESO DE 48 D\u00cdGITOS\n// ============================================================\n\nconst clave48 =\n fechaEmisionStr +\n tipoComprobante +\n ruc +\n ambiente +\n establecimiento +\n puntoEmision +\n secuencial +\n codigoNumerico +\n tipoEmision;\n\n// ============================================================\n// M\u00d3DULO 11\n// ============================================================\n\nfunction calcularDigitoVerificadorModulo11(clave) {\n\n const factores = [\n 2,\n 3,\n 4,\n 5,\n 6,\n 7,\n ];\n\n let factorIndex = 0;\n let suma = 0;\n\n for (\n let i = clave.length - 1;\n i >= 0;\n i--\n ) {\n\n const digito = parseInt(\n clave[i],\n 10\n );\n\n suma +=\n digito *\n factores[factorIndex];\n\n factorIndex =\n (factorIndex + 1) %\n factores.length;\n }\n\n const modulo =\n suma % 11;\n\n let verificador =\n 11 - modulo;\n\n if (verificador === 11) {\n verificador = 0;\n }\n\n if (verificador === 10) {\n verificador = 1;\n }\n\n return String(\n verificador\n );\n}\n\n// ============================================================\n// CLAVE DE ACCESO FINAL\n// ============================================================\n\nconst digitoVerificador =\n calcularDigitoVerificadorModulo11(\n clave48\n );\n\nconst claveAcceso =\n clave48 +\n digitoVerificador;\n\n// ============================================================\n// VALIDAR LONGITUD\n// ============================================================\n\nif (\n claveAcceso.length !== 49\n) {\n throw new Error(\n `La clave de acceso generada no tiene 49 digitos: ${claveAcceso.length}`\n );\n}\n\n// ============================================================\n// SALIDA\n// ============================================================\n\nreturn [\n {\n json: {\n claveAcceso,\n secuencial,\n factura,\n },\n },\n];"
},
"id": "1a761dc1-0f79-4e69-bcca-daff604ec2fe",
"name": "Generar Clave de Acceso",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-1200,
-512
]
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:4000/api/sri/recepcion",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ { \"claveAcceso\": $json.claveAcceso, \"factura\": $json.factura } }}",
"options": {}
},
"id": "6aa37cfa-bd80-4059-906a-7489a3d36943",
"name": "SRI Recepcion",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-1024,
-512
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
},
"conditions": [
{
"id": "cond-recepcion-ok",
"leftValue": "={{ $json.estado }}",
"rightValue": "RECIBIDA",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "ec94f359-2a3c-4f57-8eb7-5df0f9e8cb4b",
"name": "Recepcion OK?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
-864,
-496
]
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:4000/api/sri/autorizacion",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ { \"claveAcceso\": $('Generar Clave de Acceso').item.json.claveAcceso } }}",
"options": {}
},
"id": "234d87f7-63f6-4aa8-bcb8-47d4fbac7044",
"name": "SRI Autorizacion",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-672,
-512
]
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:4000/api/sri/generar-xml",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ { \"claveAcceso\": $('Generar Clave de Acceso').item.json.claveAcceso, \"factura\": $('Generar Clave de Acceso').item.json.factura } }}",
"options": {}
},
"id": "243fa2bf-94cb-4fbb-bcca-16878801ee18",
"name": "SRI Generar XML",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-512,
-512
]
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:4000/api/sri/generar-ride",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ {\n \"claveAcceso\": $('Generar Clave de Acceso').item.json.claveAcceso,\n \"factura\": $('Generar Clave de Acceso').item.json.factura\n} }}",
"options": {}
},
"id": "86580e13-82c3-4517-84d2-1567eb990701",
"name": "SRI Generar RIDE",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-352,
-512
]
},
{
"parameters": {
"jsCode": "// Captura segura de datos previos usando .first()\nconst clave = $('Generar Clave de Acceso').first()?.json?.claveAcceso || '';\nconst factura = $('Generar Clave de Acceso').first()?.json?.factura || $('Webhook Recibir Factura').first()?.json?.body || {};\nconst autorizacion = $('SRI Autorizacion').first()?.json || {};\nconst xml = $('SRI Generar XML').first()?.json || {};\nconst ride = $('SRI Generar RIDE').first()?.json || {};\n\n// Capturamos la IA de forma ultra-segura desde el nodo AI Agent\nlet categoriaIa = 'General';\nlet asuntoEmail = '';\nlet cuerpoEmail = '';\n\ntry {\n const aiItem = $('AI Agent').first()?.json;\n if (aiItem && aiItem.output) {\n const aiOutput = aiItem.output;\n const aiData = typeof aiOutput === 'string' ? JSON.parse(aiOutput) : aiOutput;\n \n categoriaIa = aiData.categoria || 'General';\n asuntoEmail = aiData.asunto_email || '';\n cuerpoEmail = aiData.cuerpo_email || '';\n }\n} catch (e) {\n console.log(\"No se pudo parsear el resultado del AI Agent, usando valores por defecto.\");\n}\n\nreturn [\n {\n json: {\n ok: true,\n claveAcceso: clave,\n factura: factura,\n numeroAutorizacion: autorizacion.numeroAutorizacion || clave,\n fechaAutorizacion: autorizacion.fechaAutorizacion || new Date().toISOString(),\n urlRide: ride.urlRide || '',\n urlXml: xml.urlXml || '',\n // Enviamos la categor\u00eda en todas sus posibles variantes de nombre:\n categoria: categoriaIa,\n categoriaIa: categoriaIa,\n categoria_ia: categoriaIa,\n asunto: asuntoEmail || 'Factura Electr\u00f3nica Emitida',\n mensaje: cuerpoEmail || 'Comprobante autorizado correctamente.',\n },\n },\n];"
},
"id": "fcae8117-460b-4322-b808-d5c3f94a4793",
"name": "Preparar Respuesta Final",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
128,
-624
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ $('Preparar Respuesta Final').item.json }}",
"options": {}
},
"id": "a30ec880-7772-421c-9a56-67f550455ea4",
"name": "Responder al Cliente",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
304,
-704
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"ok\": false, \"mensaje\": \"El SRI simulado rechazo el comprobante. Intenta nuevamente.\" } }}",
"options": {
"responseCode": 502
}
},
"id": "e3fe27a2-01de-4a9a-92df-f19ed0b66e41",
"name": "Responder Error",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
-720,
-336
]
},
{
"parameters": {
"fromEmail": "kam.santana@yavirac.edu.ec",
"toEmail": "={{ $('Preparar Respuesta Final').first().json.factura.cliente.correo }}",
"subject": "={{ 'Tu factura electr\u00f3nica - Clave de acceso ' + $('Preparar Respuesta Final').item.json.claveAcceso }}",
"html": "={{ $('Preparar Respuesta Final').first().json.mensaje }}",
"options": {
"attachments": "rideData, xmlData"
}
},
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [
656,
-496
],
"id": "bce40f6a-f669-454a-812b-d46927628031",
"name": "Send an Email",
"executeOnce": true,
"credentials": {
"smtp": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"promptType": "define",
"text": "=Procesa la compra del cliente {{ $('Webhook Recibir Factura').first().json.body.cliente.nombre }} en \"PROVEEDOR FERRETERO PROFERRET S.A.S.\".\n\n\u00cdtems comprados:\n{{ JSON.stringify($('Webhook Recibir Factura').first().json.body.items) }}\n\nDetermina la categor\u00eda (Herramientas, Servicios, Insumos o General) y genera el JSON de salida con el mensaje para el correo.",
"options": {
"systemMessage": "Eres el asistente virtual de Inteligencia Artificial de \"PROVEEDOR FERRETERO PROFERRET S.A.S.\". Tu trabajo es analizar los \u00edtems de una factura y generar un JSON con la categor\u00eda de la compra y un mensaje amigable para el correo del cliente.\n\nREGLA OBLIGATORIA: Responde \u00daNICAMENTE un JSON v\u00e1lido sin bloques de c\u00f3digo Markdown (no incluyas ```json ni comillas alrededor del bloque).\n\nEstructura requerida:\n{\n \"asunto_email\": \"\u00a1Gracias por tu compra en PROFERRET S.A.S.!\",\n \"cuerpo_email\": \"Estimado/a cliente, adjunto a este correo encontrar\u00e1 su comprobante electr\u00f3nico autorizado por el SRI. \u00a1Agradecemos su preferencia!\",\n \"categoria\": \"<CATEGORIA>\"\n}\n\nReglas estrictas para <CATEGORIA>:\n- \"Insumos\": Pinturas, brochas, lijas, clavos, tornillos, tubos, cables, cemento, pegamentos o consumibles.\n- \"Herramientas\": Taladros, martillos, alicates, sierras, llaves, herramientas manuales o el\u00e9ctricas.\n- \"Servicios\": Mano de obra, fletes, alquileres, instalaciones.\n- \"General\": Solo si no encaja en las anteriores."
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3.1,
"position": [
-192,
-512
],
"id": "9682437f-8ff7-4752-8055-9a9a1f6e30c7",
"name": "AI Agent"
},
{
"parameters": {
"model": "llama-3.1-8b-instant",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGroq",
"typeVersion": 1,
"position": [
-192,
-272
],
"id": "7ac9ea7b-8027-49b7-b3e4-0b119485484e",
"name": "Groq Chat Model",
"credentials": {
"groqApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:4000/api/sri/actualizar-ia",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"claveAcceso\": \"{{ $('SRI Autorizacion').first().json.claveAcceso || $('Generar Clave de Acceso').first().json.claveAcceso }}\",\n \"categoriaIa\": \"{{ (() => { const raw = $json.output || $json.text || ''; if (raw.includes('Herramientas')) return 'Herramientas'; if (raw.includes('Servicios')) return 'Servicios'; if (raw.includes('Insumos')) return 'Insumos'; return 'General'; })() }}\",\n \"estadoIa\": \"OK\",\n \"iaOutput\": \"Clasificado exitosamente por el Agente IA Groq\"\n}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
128,
-384
],
"id": "ac80de2e-ca03-43c3-94b6-285d0d9212eb",
"name": "HTTP Request"
}
],
"connections": {
"Descargar RIDE (PDF)": {
"main": [
[
{
"node": "Descargar XML",
"type": "main",
"index": 0
}
]
]
},
"Descargar XML": {
"main": [
[
{
"node": "Send an Email",
"type": "main",
"index": 0
},
{
"node": "Respaldar RIDE en Drive",
"type": "main",
"index": 0
}
]
]
},
"Respaldar RIDE en Drive": {
"main": [
[
{
"node": "Respaldar XML en Drive",
"type": "main",
"index": 0
}
]
]
},
"Webhook Recibir Factura": {
"main": [
[
{
"node": "Generar Clave de Acceso",
"type": "main",
"index": 0
}
]
]
},
"Generar Clave de Acceso": {
"main": [
[
{
"node": "SRI Recepcion",
"type": "main",
"index": 0
}
]
]
},
"SRI Recepcion": {
"main": [
[
{
"node": "Recepcion OK?",
"type": "main",
"index": 0
}
]
]
},
"Recepcion OK?": {
"main": [
[
{
"node": "SRI Autorizacion",
"type": "main",
"index": 0
}
],
[
{
"node": "Responder Error",
"type": "main",
"index": 0
}
]
]
},
"SRI Autorizacion": {
"main": [
[
{
"node": "SRI Generar XML",
"type": "main",
"index": 0
}
]
]
},
"SRI Generar XML": {
"main": [
[
{
"node": "SRI Generar RIDE",
"type": "main",
"index": 0
}
]
]
},
"SRI Generar RIDE": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Preparar Respuesta Final": {
"main": [
[
{
"node": "Responder al Cliente",
"type": "main",
"index": 0
},
{
"node": "Descargar RIDE (PDF)",
"type": "main",
"index": 0
}
]
]
},
"Respaldar XML en Drive": {
"main": [
[]
]
},
"AI Agent": {
"main": [
[
{
"node": "Preparar Respuesta Final",
"type": "main",
"index": 0
},
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Groq Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Send an Email": {
"main": [
[]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate"
},
"versionId": "a8c1ea88-7705-44a4-b14f-411bc285d3d4",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "IONb602IFhwXa2W0",
"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.
googleDriveOAuth2ApigroqApismtp
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
1 - Emision y Autorizacion en Tiempo Real. Uses httpRequest, googleDrive, emailSend, agent. Webhook trigger; 18 nodes.
Source: https://github.com/kamsantana/PROYECTO-FACTURACION-N8N/blob/main/n8n-workflow/workflow-1-emision-y-autorizacion.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.
Automatically transforms your travel photos and notes into beautiful journals, highlight reels, and review drafts using Claude's vision and language capabilities. Trip Completion Trigger - Webhook or
This workflow automates pre-dispatch customs document validation for international shipments. It ingests shipping document packages, extracts content from each file, uses Claude AI to cross-validate a
This workflow ingests property document packages submitted via webhook or monitored cloud storage, extracts text from each file, runs Claude AI to verify legal compliance, detect missing or expired do
Automatically converts your daily WhatsApp messages and photos from travels into beautifully structured travel stories, saved as documents in Google Drive. Receive WhatsApp Updates - Webhook captures
L&D_AgentsAI_ATIVO. Uses httpRequest, agent, googleCalendarTool, toolSerpApi. Webhook trigger; 93 nodes.