AutomationFlowsMarketing & Ads › Sms+mail Campaña - Wf2 Envio Mail (30 Min Despues Del Sms)

Sms+mail Campaña - Wf2 Envio Mail (30 Min Despues Del Sms)

SMS+Mail Campaña - WF2 Envio Mail (30 min despues del SMS). Uses googleSheets, readWriteFile, emailSend. Scheduled trigger; 18 nodes.

Cron / scheduled trigger★★★★☆ complexity18 nodesGoogle SheetsRead Write FileEmail Send
Marketing & Ads Trigger: Cron / scheduled Nodes: 18 Complexity: ★★★★☆ Added:

This workflow follows the Emailsend → Google Sheets 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": "SMS+Mail Campa\u00f1a - WF2 Envio Mail (30 min despues del SMS)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "*/10 * * * *"
            }
          ]
        }
      },
      "id": "schedule_trigger",
      "name": "Schedule cada 10 min",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        300
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "1",
              "name": "GOOGLE_SHEET_ID",
              "type": "string",
              "value": "REEMPLAZAR - ID del ENVIO_SMS_MAIL_V2 en Google Sheets"
            },
            {
              "id": "2",
              "name": "N8N_BASE_URL",
              "type": "string",
              "value": "https://devn8n.gruporoisa.com.ar"
            },
            {
              "id": "3",
              "name": "TEMPLATES_DIR",
              "type": "string",
              "value": "REEMPLAZAR - carpeta accesible por n8n con plantilla_alta.html/plantilla_media.html/plantilla_baja.html"
            },
            {
              "id": "4",
              "name": "MAIL_FROM",
              "type": "string",
              "value": "ventasdoctored@doctored.com.ar"
            }
          ]
        },
        "options": {}
      },
      "id": "set_config",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        300
      ],
      "notesInFlow": true,
      "notes": "TEMPLATES_DIR debe ser accesible por el proceso de n8n (mismo host o volumen montado). Si n8n corre en otro servidor, reemplazar 'Leer Template HTML' por un HTTP Request a donde esten hosteados los .html."
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.GOOGLE_SHEET_ID }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Control_Data"
        },
        "options": {}
      },
      "id": "read_control_data",
      "name": "Leer Estado Campa\u00f1a",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        440,
        300
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const rows = $input.all().map(i => i.json);\nconst estado = (rows.find(r => r.clave === 'Estado_Campana') || {}).valor || 'Pausada';\nconst activa = estado.toString().trim().toLowerCase() === 'activa';\nreturn [{ json: { estadoCampana: estado, activa } }];"
      },
      "id": "check_estado",
      "name": "Chequear Estado Campa\u00f1a",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.activa }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if_activa",
      "name": "\u00bfCampa\u00f1a Activa?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        880,
        300
      ]
    },
    {
      "parameters": {},
      "id": "noop_pausada",
      "name": "Campa\u00f1a Pausada - No enviar",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        1100,
        500
      ]
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config').first().json.GOOGLE_SHEET_ID }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Plantillas"
        },
        "options": {}
      },
      "id": "read_plantillas",
      "name": "Leer Plantillas",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1100,
        160
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config').first().json.GOOGLE_SHEET_ID }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Plantillas_Mail"
        },
        "options": {}
      },
      "id": "read_plantillas_mail",
      "name": "Leer Plantillas_Mail",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1100,
        260
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config').first().json.GOOGLE_SHEET_ID }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Contactos"
        },
        "options": {}
      },
      "id": "read_contacts",
      "name": "Leer Contactos",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1100,
        360
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "function diffMin(fechaStr) {\n  if (!fechaStr) return -1;\n  const f = new Date(fechaStr.toString().replace(' ', 'T'));\n  if (isNaN(f.getTime())) return -1;\n  return (Date.now() - f.getTime()) / 60000;\n}\n\nconst tandas = $('Leer Plantillas').all().map(i => i.json).sort((a, b) => Number(a.tanda) - Number(b.tanda));\n// Plantillas_Mail esta vinculada a la tanda (no al lead): cada tanda define que .html usar.\nconst plantillasMail = $('Leer Plantillas_Mail').all().map(i => i.json);\n\nconst out = [];\nfor (const item of $input.all()) {\n  const j = item.json;\n  if ((j['desuscripto'] || '').toString().trim().toLowerCase() === 'si') continue;\n  if (!j.email) continue;\n\n  for (const t of tandas) {\n    const n = Number(t.tanda);\n    const envioN = (j['Envio' + n] || '').toString().trim();\n    const emailEnvioN = (j['EmailEnvio' + n] || '').toString().trim();\n    if (envioN !== 'Si') continue;      // el SMS de esta tanda no se mando (o fallo)\n    if (emailEnvioN !== '') continue;   // el mail de esta tanda ya se envio\n\n    const delayMin = Number(t['delay_mail_min'] || 30);\n    if (diffMin(j['FechaEnvio' + n]) < delayMin) continue; // no paso la ventana de espera\n\n    const plantillaMail = plantillasMail.find(p => Number(p.tanda) === n);\n    if (!plantillaMail || !plantillaMail.archivo_template) continue; // tanda sin template de mail configurado -> no se envia\n\n    out.push({\n      json: {\n        row_number: j.row_number,\n        nombre: j.nombre || '',\n        email: j.email,\n        codigo: j.codigo,\n        tanda: n,\n        archivo_template: plantillaMail.archivo_template\n      }\n    });\n    break; // 1 mail por corrida por lead; las tandas restantes se procesan en la proxima corrida\n  }\n}\nreturn out;"
      },
      "id": "filter_candidatos_mail",
      "name": "Generar candidatos mail",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1320,
        300
      ]
    },
    {
      "parameters": {
        "operation": "read",
        "fileSelector": "={{ $('Config').first().json.TEMPLATES_DIR }}/{{ $json.archivo_template }}"
      },
      "id": "read_template_file",
      "name": "Leer Template HTML",
      "type": "n8n-nodes-base.readWriteFile",
      "typeVersion": 1,
      "position": [
        1540,
        300
      ],
      "notesInFlow": true,
      "notes": "Lee el .html en disco. Requiere que n8n tenga acceso al filesystem donde estan plantilla_alta/media/baja.html."
    },
    {
      "parameters": {
        "operation": "text",
        "binaryPropertyName": "data"
      },
      "id": "extract_template_text",
      "name": "Extraer Texto Template",
      "type": "n8n-nodes-base.extractFromFile",
      "typeVersion": 1,
      "position": [
        1760,
        300
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const j = $json;\nconst n8nBase = $('Config').first().json.N8N_BASE_URL;\nconst unsubUrl = `${n8nBase}/webhook/mail-baja/${j.codigo}`;\nconst clickUrl = `${n8nBase}/webhook/mail-click/${j.codigo}-${j.tanda}`;\n\nlet html = j.text || j.data || '';\nhtml = html.replace(/\\[email\\]/g, j.email);\n// Reemplaza el href del link \"quiero desuscribirme\" (identificado por su id fijo en el template).\nhtml = html.replace(/(href=\")[^\"]*(\"[^>]*id=\"unlayer-custom-tool-footer-unsubscribe-link\")/, `$1${unsubUrl}$2`);\n// Reemplaza el href del boton CTA principal (identificado por su clase fija en el template).\nhtml = html.replace(/(href=\")[^\"]*(\"[^>]*class=\"v-button\")/, `$1${clickUrl}$2`);\n\nreturn { json: { row_number: j.row_number, tanda: j.tanda, email: j.email, html } };"
      },
      "id": "armar_mail",
      "name": "Armar Mail (placeholders + tracking)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1980,
        300
      ],
      "notesInFlow": true,
      "notes": "El matching de 'class=\"v-button\"' y del id de desuscripcion depende del markup actual de plantilla_alta/media/baja.html. Revisar si se edita el dise\u00f1o del template en el editor de mails."
    },
    {
      "parameters": {
        "fromEmail": "={{ $('Config').first().json.MAIL_FROM }}",
        "toEmail": "={{ $json.email }}",
        "subject": "En DoctoRED queremos ayudarte a encontrar tu plan ideal",
        "html": "={{ $json.html }}",
        "options": {}
      },
      "id": "send_mail",
      "name": "Enviar Mail (SMTP doctored.dyndns.org)",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        2200,
        300
      ],
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.messageId }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if_mail_success",
      "name": "Envio Mail OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2420,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const j = $('Armar Mail (placeholders + tracking)').item.json;\nconst n = j.tanda;\nconst out = { row_number: j.row_number };\nout['EmailEnvio' + n] = new Date().toISOString().slice(0, 19).replace('T', ' ');\nreturn [{ json: out }];"
      },
      "id": "prep_update_mail_ok",
      "name": "Preparar Update - Exito",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2640,
        220
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Config').first().json.GOOGLE_SHEET_ID }}"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Contactos"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "matchingColumns": [
            "row_number"
          ],
          "value": {}
        },
        "options": {}
      },
      "id": "update_success",
      "name": "Actualizar fila - Exito",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2860,
        220
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {},
      "id": "noop_mail_fail",
      "name": "No marcar - reintenta proxima corrida",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        2640,
        400
      ],
      "notesInFlow": true,
      "notes": "No se escribe EmailEnvio{N}: al quedar vacio, la proxima corrida de este mismo workflow lo vuelve a intentar."
    }
  ],
  "connections": {
    "Schedule cada 10 min": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config": {
      "main": [
        [
          {
            "node": "Leer Estado Campa\u00f1a",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Leer Estado Campa\u00f1a": {
      "main": [
        [
          {
            "node": "Chequear Estado Campa\u00f1a",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chequear Estado Campa\u00f1a": {
      "main": [
        [
          {
            "node": "\u00bfCampa\u00f1a Activa?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u00bfCampa\u00f1a Activa?": {
      "main": [
        [
          {
            "node": "Leer Plantillas",
            "type": "main",
            "index": 0
          },
          {
            "node": "Leer Plantillas_Mail",
            "type": "main",
            "index": 0
          },
          {
            "node": "Leer Contactos",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Campa\u00f1a Pausada - No enviar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Leer Contactos": {
      "main": [
        [
          {
            "node": "Generar candidatos mail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generar candidatos mail": {
      "main": [
        [
          {
            "node": "Leer Template HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Leer Template HTML": {
      "main": [
        [
          {
            "node": "Extraer Texto Template",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extraer Texto Template": {
      "main": [
        [
          {
            "node": "Armar Mail (placeholders + tracking)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Armar Mail (placeholders + tracking)": {
      "main": [
        [
          {
            "node": "Enviar Mail (SMTP doctored.dyndns.org)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enviar Mail (SMTP doctored.dyndns.org)": {
      "main": [
        [
          {
            "node": "Envio Mail OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Envio Mail OK?": {
      "main": [
        [
          {
            "node": "Preparar Update - Exito",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No marcar - reintenta proxima corrida",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparar Update - Exito": {
      "main": [
        [
          {
            "node": "Actualizar fila - Exito",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

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

SMS+Mail Campaña - WF2 Envio Mail (30 min despues del SMS). Uses googleSheets, readWriteFile, emailSend. Scheduled trigger; 18 nodes.

Source: https://github.com/martinrodrigogerez/quickcampaign_sms_mail/blob/13e6d1b38329da388efeb07c608d1048e363fc47/n8n/WF2_envio_mail.json — original creator credit. Request a take-down →

More Marketing & Ads workflows → · Browse all categories →

Related workflows

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

Marketing & Ads

Automate personalized email campaigns using a Google Sheets contact list, a Google Docs template, and SMTP delivery. Google Docs is used as the email template with variables: , , , . Google Sheet cont

Google Sheets, Google Docs, Email Send
Marketing & Ads

Automatización de Email Marketing. Uses googleSheets, emailSend. Scheduled trigger; 4 nodes.

Google Sheets, Email Send
Marketing & Ads

Workflow-Lead-Summary. Uses googleSheets, emailSend. Scheduled trigger; 3 nodes.

Google Sheets, Email Send
Marketing & Ads

Lead Gen -> Outreach -> Reply Digest. Uses googleSheets, httpRequest, emailSend, emailReadImap. Event-driven trigger; 36 nodes.

Google Sheets, HTTP Request, Email Send +1
Marketing & Ads

This workflow runs on scheduled weekly and monthly triggers to generate unified marketing performance reports. It processes multiple websites by collecting analytics data, paid ads performance, and CR

Gmail, Google Sheets, Google Analytics +3