CRM de GSheets

Avanzado

Este es unCRM, Multimodal AIflujo de automatización del dominio deautomatización que contiene 24 nodos.Utiliza principalmente nodos como If, Set, Code, Gmail, Webhook. De prospecto a cliente automatizado usando flujos de Google Sheets, notificaciones por correo y seguimiento de tiempo

Requisitos previos
  • Cuenta de Google y credenciales de API de Gmail
  • Punto final de HTTP Webhook (n8n generará automáticamente)
  • Credenciales de API de Google Sheets
Vista previa del flujo de trabajo
Visualización de las conexiones entre nodos, con soporte para zoom y panorámica
Exportar flujo de trabajo
Copie la siguiente configuración JSON en n8n para importar y usar este flujo de trabajo
{
  "id": "sbTxG7FVDyFq0sLR",
  "meta": {
    "instanceId": "{{INSTANCE_ID}}",
    "templateCredsSetupCompleted": true
  },
  "name": "GSheets CRM",
  "tags": [],
  "nodes": [
    {
      "id": "c0760f3c-0feb-43bb-9b52-cc9307e0b98f",
      "name": "Webhook: Etapa de Prospecto Cambiada",
      "type": "n8n-nodes-base.webhook",
      "notes": "Receives JSON when a lead's Stage (Leads!E) changes.\nExpected body keys:\n- name (Leads!A)\n- email (Leads!C)\n- source_id (for mapping 1=Instagram,2=Facebook) OR source_text\n- stage (new stage text)\n- previous_stage (optional)",
      "position": [
        -896,
        -80
      ],
      "webhookId": "{{WEBHOOK_ID_LEAD_STAGE_CHANGED}}",
      "parameters": {
        "path": "lead-stage-changed",
        "options": {
          "responseData": "OK"
        },
        "httpMethod": "POST"
      },
      "typeVersion": 1
    },
    {
      "id": "a91971ec-9414-4d0d-98d9-273761071c30",
      "name": "Preparar Campos de Correo",
      "type": "n8n-nodes-base.set",
      "notes": "Derives firstName and sourcePlatform for the email body.",
      "position": [
        -672,
        -80
      ],
      "parameters": {
        "fields": {
          "values": [
            {
              "name": "firstName",
              "stringValue": "=={{ $json.body.name ? $json.body.name.trim().split(' ')[0] : 'there' }}"
            },
            {
              "name": "sourcePlatform ",
              "stringValue": "={{ $json.body.source_text || 'Unknown' }}"
            },
            {
              "name": "stage",
              "stringValue": "={{ $json.body.stage }}"
            },
            {
              "name": "previousStage",
              "stringValue": "={{ $json.body.previous_stage || '' }}"
            }
          ]
        },
        "options": {
          "includeBinary": false
        }
      },
      "typeVersion": 3
    },
    {
      "id": "00ef748d-22e8-4436-a0e7-7ef1ed9017fe",
      "name": "Gmail: Enviar Correo de Cambio de Etapa",
      "type": "n8n-nodes-base.gmail",
      "notes": "Sends the templated email on any Stage change.",
      "position": [
        -224,
        -176
      ],
      "webhookId": "{{GMAIL_NODE_WEBHOOK_ID_STAGE_CHANGE}}",
      "parameters": {
        "sendTo": "={{ $('Prepare Email Fields').item.json.body.email }}",
        "message": "=Hi {{ $('Prepare Email Fields').item.json.body.name }},<br><br>Thanks for the call earlier & happy I was able to meet you. Just letting you know that I'll work on a proposal immediately and have something over to you in the next 2-3 hours.<br><br>Stay tuned & let me know if you have any questions.<br><br>Thanks,<br>{{SENDER_NAME}}",
        "options": {},
        "subject": "Next Steps"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "{{GMAIL_CREDENTIAL_ID}}",
          "name": "{{GMAIL_CREDENTIAL_NAME}}"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "baa49e6a-3c2e-4c42-ae96-9b2f0e5a0db3",
      "name": "IF: Etapa == Ganado",
      "type": "n8n-nodes-base.if",
      "notes": "Branch for when the lead has been marked Won.",
      "position": [
        -224,
        16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "5feb2d51-b3ba-4b5e-9064-4735e10d5a38",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.stage }}",
              "rightValue": "Won"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "855b796a-8588-4f43-8b39-48406c37c67d",
      "name": "Formatear Marca de Tiempo de Inicio",
      "type": "n8n-nodes-base.dateTime",
      "notes": "Produces Start Date & Time as: dd/mm/yyyy at HH:MM",
      "position": [
        0,
        16
      ],
      "parameters": {
        "value": "={{ $now }}",
        "custom": true,
        "options": {},
        "toFormat": "DD/MM/YYYY 'at' HH:mm"
      },
      "typeVersion": 1
    },
    {
      "id": "25f0b4f3-0305-4ce6-83f8-e37fc90d51ca",
      "name": "GS: Agregar Cliente (Al Ganar)",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Appends the new client with Project Status=In Progress and Start Date & Time.",
      "position": [
        224,
        16
      ],
      "parameters": {
        "columns": {
          "value": {
            "Name": "={{ $json.body.name }}",
            "Client Email": "={{ $json.body.email }}",
            "Start Date & Time": "={{ $json.data }}"
          },
          "schema": [
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Client Type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Client Type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Client Email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Client Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Project Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Project Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Tenure",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Tenure",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Start Date & Time",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Start Date & Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "End Date & Time",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "End Date & Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Time to Deliver",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Time to Deliver",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "useAppend": true,
          "cellFormat": "USER_ENTERED"
        },
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "{{CLIENTS_GID}}",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/{{GOOGLE_SHEETS_DOC_ID}}/edit#gid={{CLIENTS_GID}}",
          "cachedResultName": "Clients"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "{{GOOGLE_SHEETS_DOC_ID}}",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/{{GOOGLE_SHEETS_DOC_ID}}/edit",
          "cachedResultName": "{{SHEET_NAME}}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "{{GSHEETS_CREDENTIAL_ID}}",
          "name": "{{GSHEETS_CREDENTIAL_NAME}}"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "100c9018-23b7-4724-93ef-373566ae9c42",
      "name": "Webhook: ¿Prospecto Calificado?",
      "type": "n8n-nodes-base.webhook",
      "notes": "Receives JSON when Leads!H (Qualified?) is checked.\nExpected body keys:\n- name (A)\n- email (C)\n- qualified (boolean or 'TRUE'/1)",
      "position": [
        -896,
        -624
      ],
      "webhookId": "{{WEBHOOK_ID_LEAD_QUALIFIED}}",
      "parameters": {
        "path": "lead-qualified",
        "options": {
          "responseData": "OK"
        },
        "httpMethod": "POST"
      },
      "typeVersion": 1
    },
    {
      "id": "e5fe57ce-c457-4f03-9c96-7ac385b2a42f",
      "name": "IF: Está Calificado",
      "type": "n8n-nodes-base.if",
      "position": [
        -672,
        -624
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "285b4bc9-5a4b-4f6b-bd55-eb54d6a6fcc1",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.body.qualified }}",
              "rightValue": "=\"true\""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "04851d87-b2c6-4383-9687-382f3bee1e7b",
      "name": "Gmail: Enviar Invitación de Cal.com",
      "type": "n8n-nodes-base.gmail",
      "notes": "Sends the booking link when Qualified? is checked.",
      "position": [
        -448,
        -624
      ],
      "webhookId": "{{GMAIL_NODE_WEBHOOK_ID_CAL_INVITE}}",
      "parameters": {
        "sendTo": "={{ $json.body.email }}",
        "message": "=Hi {{$json.body.name ? $json.body.name.split(' ')[0] : 'there'}},<br><br>Congrats — you’re qualified and we are super excited to know more from you! Please book your discovery call here: <a href=\"{{CAL_COM_BOOKING_URL}}\" target=\"_blank\">Schedule on Cal.com</a>.<br><br>Thanks,<br>{{SENDER_NAME}}",
        "options": {},
        "subject": "Book Your Discovery Call"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "{{GMAIL_CREDENTIAL_ID}}",
          "name": "{{GMAIL_CREDENTIAL_NAME}}"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "99f6f896-ac20-454b-a480-88a71aabe0b0",
      "name": "GS: Actualizar Etapa de Prospectos a Reunión Agendada",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Uses Email (Column C) as key to update Stage (E) to 'Meeting Booked'.",
      "position": [
        -672,
        -368
      ],
      "parameters": {
        "columns": {
          "value": {
            "Stage": "Meeting Booked",
            "Client Email": "={{ $json.body.payload.attendees[0].email }}"
          },
          "schema": [
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date Updated",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Date Updated",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Client Email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Client Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Lead Source",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Lead Source",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Stage",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Stage",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Asignee",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Asignee",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Discovery Call URL",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Discovery Call URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Qualitfied?",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Qualitfied?",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Client Email"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "cellFormat": "USER_ENTERED"
        },
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid={{LEADS_GID}}",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/{{GOOGLE_SHEETS_DOC_ID}}/edit#gid={{LEADS_GID}}",
          "cachedResultName": "Leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "{{GOOGLE_SHEETS_DOC_ID}}",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/{{GOOGLE_SHEETS_DOC_ID}}/edit",
          "cachedResultName": "{{SHEET_NAME}}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "{{GSHEETS_CREDENTIAL_ID}}",
          "name": "{{GSHEETS_CREDENTIAL_NAME}}"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "b2acf148-b6bc-49d3-8e76-fe890459c1dc",
      "name": "Webhook: Estado de Cliente Cambiado",
      "type": "n8n-nodes-base.webhook",
      "notes": "Receives JSON when Clients!D (Project Status) changes.\nExpected body keys:\n- email (Clients!C)\n- project_status (Clients!D new value)",
      "position": [
        -880,
        304
      ],
      "webhookId": "{{WEBHOOK_ID_CLIENT_STATUS_CHANGED}}",
      "parameters": {
        "path": "client-status-changed",
        "options": {
          "responseData": "OK"
        },
        "httpMethod": "POST"
      },
      "typeVersion": 1
    },
    {
      "id": "01223e18-22c3-4622-ba66-0425aad4bcd6",
      "name": "IF: ¿Entregado?",
      "type": "n8n-nodes-base.if",
      "position": [
        -656,
        304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "8be5b372-0114-4595-ab9a-d7903d63a716",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.body.project_status }}",
              "rightValue": "Delivered"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "438ffb35-490f-49d1-9031-c32447b59992",
      "name": "GS: Buscar Cliente por Correo",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Fetches the row to read Start Date & Time (F) for duration calc.",
      "position": [
        -432,
        304
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $json.body.email }}",
              "lookupColumn": "Client Email"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "{{CLIENTS_GID}}",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/{{GOOGLE_SHEETS_DOC_ID}}/edit#gid={{CLIENTS_GID}}",
          "cachedResultName": "Clients"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "{{GOOGLE_SHEETS_DOC_ID}}",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/{{GOOGLE_SHEETS_DOC_ID}}/edit",
          "cachedResultName": "{{SHEET_NAME}}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "{{GSHEETS_CREDENTIAL_ID}}",
          "name": "{{GSHEETS_CREDENTIAL_NAME}}"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "1579d825-5a67-4e52-b7d5-f4dc493cf790",
      "name": "Formatear Marca de Tiempo de Fin",
      "type": "n8n-nodes-base.dateTime",
      "notes": "End Date & Time formatted as dd/mm/yyyy at HH:MM",
      "position": [
        -208,
        304
      ],
      "parameters": {
        "value": "={{ $now }}",
        "custom": true,
        "options": {},
        "toFormat": "DD/MM/YYYY 'at' HH:mm"
      },
      "typeVersion": 1
    },
    {
      "id": "1aec66f5-ce82-4375-9202-a3ba2a5ad118",
      "name": "GS: Actualizar Fin y Duración de Clientes",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Sets End Date & Time (G) and Time To Deliver (H).",
      "position": [
        240,
        304
      ],
      "parameters": {
        "columns": {
          "value": {
            "Client Email": "={{ $json[\"Client Email\"] }}",
            "End Date & Time": "={{ $json.endStr }}",
            "Time to Deliver": "={{ $json.duration }}"
          },
          "schema": [
            {
              "id": "Name",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Client Type",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Client Type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Client Email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Client Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Project Status",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Project Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Tenure",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Tenure",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Start Date & Time",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Start Date & Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "End Date & Time",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "End Date & Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Time to Deliver",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Time to Deliver",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Client Email"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "cellFormat": "USER_ENTERED"
        },
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "{{CLIENTS_GID}}",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/{{GOOGLE_SHEETS_DOC_ID}}/edit#gid={{CLIENTS_GID}}",
          "cachedResultName": "Clients"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "{{GOOGLE_SHEETS_DOC_ID}}",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/{{GOOGLE_SHEETS_DOC_ID}}/edit",
          "cachedResultName": "{{SHEET_NAME}}"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "{{GSHEETS_CREDENTIAL_ID}}",
          "name": "{{GSHEETS_CREDENTIAL_NAME}}"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "1f2a3e16-70c7-402f-ace7-782f9de34fd3",
      "name": "Código",
      "type": "n8n-nodes-base.code",
      "position": [
        16,
        304
      ],
      "parameters": {
        "jsCode": "// Compute delivery duration from \"Start Date & Time\" (F) and formatted \"End\" from previous node\n// Expects this node to run AFTER \"Format End Timestamp\" and AFTER the Clients row lookup\n\nconst items = $input.all();\n\n// Helper: parse \"dd/mm/yyyy at HH:MM\"\nfunction parseDT(s) {\n  if (!s) return null;\n  const m = String(s).match(/(\\d{2})\\/(\\d{2})\\/(\\d{4}).*?(\\d{2}):(\\d{2})/);\n  if (!m) return null;\n  const [, d, M, y, h, min] = m;\n  return new Date(Number(y), Number(M) - 1, Number(d), Number(h), Number(min), 0);\n}\n\n// Get the formatted end timestamp from the Date & Time node\nconst endStrFromNode = $node[\"Format End Timestamp\"]?.json?.data || \"\";\n\nfor (const item of items) {\n  const row = item.json || {};\n\n  // Try several keys for Start column F (depending on how Google Sheets node labeled it)\n  const startStr =\n    row[\"Start Date & Time\"] ??\n    row[\"F\"] ??\n    row[\"Start\"] ??\n    \"\";\n\n  // Prefer the value from Date & Time node; fall back to any end value already present\n  const endStr = endStrFromNode || row[\"End Date & Time\"] || \"\";\n\n  const start = parseDT(startStr);\n  const end = parseDT(endStr);\n\n  let duration = \"N/A\";\n  if (start && end) {\n    const ms = end - start;\n    const days = Math.floor(ms / 86_400_000);\n    const hrs = Math.floor((ms % 86_400_000) / 3_600_000);\n    const mins = Math.floor((ms % 3_600_000) / 60_000);\n    duration = `${days}d ${hrs}h ${mins}m`;\n  }\n\n  // Attach results to the item\n  item.json.endStr = endStr;\n  item.json.duration = duration;\n}\n\nreturn items;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "12c036dc-4e13-4bfd-95d2-48da0e8337b6",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        -448,
        -80
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "37fbb2ef-5861-4fc8-ab06-71fe4364be63",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.stage }}",
              "rightValue": "Awaiting Proposal"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ec815439-bfb1-4edb-8a70-9db1c167a868",
      "name": "Webhook: Reunión Agendada",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -896,
        -368
      ],
      "webhookId": "{{WEBHOOK_ID_MEETING_BOOKED}}",
      "parameters": {
        "path": "cal-booked",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "26784981-b9f6-44ca-8329-62072dd4fd0f",
      "name": "Nota Adhesiva",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1232,
        -848
      ],
      "parameters": {
        "width": 1968,
        "height": 1408,
        "content": "# Google Sheets Automated CRM"
      },
      "typeVersion": 1
    },
    {
      "id": "815c2626-dd90-4976-92d7-ce93bad40e1e",
      "name": "Nota Adhesiva1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2080,
        -848
      ],
      "parameters": {
        "color": 6,
        "width": 816,
        "height": 4128,
        "content": "# The App Script Needed (Use this for your Google Worksheet App Script)\n\n/**\n * ScaleFlow CRM — Google Apps Script bridge to n8n\n * Spreadsheet: \"ScaleFlow CRM\"\n * Tabs: \"Leads\", \"Clients\"\n *\n * WHAT IT DOES\n * - When a Lead’s Stage (Leads!E) changes → POST to n8n /lead-stage-changed\n * - When Qualified? checkbox (Leads!H) is edited → POST to n8n /lead-qualified\n * - When a Client’s Project Status (Clients!D) changes → POST to n8n /client-status-changed\n *\n * STEPS\n * 1) Paste this into Extensions → Apps Script.\n * 2) Replace the WEBHOOK URLs below with your n8n endpoints.\n * 3) Save. First edit will prompt for authorization. (Optional) Run createInstallableTrigger() once.\n */\n\nconst WEBHOOKS = {\n  STAGE_CHANGED: 'https://{{YOUR_N8N_DOMAIN}}/webhook/lead-stage-changed',\n  LEAD_QUALIFIED: 'https://{{YOUR_N8N_DOMAIN}}/webhook/lead-qualified',\n  CLIENT_STATUS_CHANGED: 'https://{{YOUR_N8N_DOMAIN}}/webhook/client-status-changed',\n};\n\nconst TABS = {\n  LEADS: 'Leads',\n  CLIENTS: 'Clients',\n};\n\n// Column indexes (1-based)\nconst COL = {\n  NAME: 1,          // Leads!A\n  CLIENT_TYPE: 2,   // Clients!B\n  EMAIL: 3,         // Leads/Clients!C\n  LEAD_SOURCE: 4,   // Leads!D\n  LEAD_STAGE: 5,    // Leads!E\n  LEAD_ASSIGNEE: 6, // Leads!F\n  PROJECT_STATUS: 4,// Clients!D\n  QUALIFIED: 8,     // Leads!H (checkbox)\n};\n\n/**\n * MAIN: fires on any cell edit\n */\nfunction onEdit(e) {\n  try {\n    if (!e || !e.range) return;\n    // Only process single-cell edits\n    if (e.range.getNumRows() !== 1 || e.range.getNumColumns() !== 1) return;\n\n    const sheet = e.range.getSheet();\n    const tabName = sheet.getName();\n    const row = e.range.getRow();\n    const col = e.range.getColumn();\n\n    // Skip header row\n    if (row === 1) return;\n\n    if (tabName === TABS.LEADS) {\n      handleLeadsEdit_(sheet, row, col, e);\n    } else if (tabName === TABS.CLIENTS) {\n      handleClientsEdit_(sheet, row, col, e);\n    }\n  } catch (err) {\n    console.error('onEdit error:', err);\n  }\n}\n\n/**\n * Handle edits on Leads tab\n */\nfunction handleLeadsEdit_(sheet, row, col, e) {\n  // Read row A..H (adjust width if you have more columns)\n  const values = sheet.getRange(row, 1, 1, 8).getValues()[0];\n  const name = asString_(values[COL.NAME - 1]);\n  const email = asString_(values[COL.EMAIL - 1]);\n  const sourceText = asString_(values[COL.LEAD_SOURCE - 1]);\n  const stageText = asString_(values[COL.LEAD_STAGE - 1]);\n  const qualifiedVal = values[COL.QUALIFIED - 1];\n\n  // 1) Stage changed (Leads!E)\n  if (col === COL.LEAD_STAGE) {\n    const payload = {\n      name,\n      email,\n      source_text: sourceText || '',\n      stage: stageText || '',\n      previous_stage: e.oldValue || '',\n      row: row,\n      tab: TABS.LEADS,\n    };\n    postJson_(WEBHOOKS.STAGE_CHANGED, payload);\n  }\n\n  // 2) Qualified? checkbox edited (Leads!H)\n  if (col === COL.QUALIFIED) {\n    const payload = {\n      name,\n      email,\n      qualified: isChecked_(qualifiedVal),\n      row: row,\n      tab: TABS.LEADS,\n    };\n    postJson_(WEBHOOKS.LEAD_QUALIFIED, payload);\n  }\n}\n\n/**\n * Handle edits on Clients tab\n */\nfunction handleClientsEdit_(sheet, row, col, e) {\n  // Read row A..H (adjust width if you have more columns)\n  const values = sheet.getRange(row, 1, 1, 8).getValues()[0];\n  const email = asString_(values[COL.EMAIL - 1]);\n  const projectStatus = asString_(values[COL.PROJECT_STATUS - 1]);\n\n  // 3) Project Status changed (Clients!D)\n  if (col === COL.PROJECT_STATUS) {\n    const payload = {\n      email,\n      project_status: projectStatus || '',\n      previous_status: e.oldValue || '',\n      row: row,\n      tab: TABS.CLIENTS,\n    };\n    postJson_(WEBHOOKS.CLIENT_STATUS_CHANGED, payload);\n  }\n}\n\n/**\n * POST JSON helper\n */\nfunction postJson_(url, obj) {\n  if (!url || url.indexOf('{{YOUR_N8N_DOMAIN}}') !== -1) {\n    console.warn('Webhook URL not set. Skipping POST:', url, obj);\n    return;\n  }\n  try {\n    const res = UrlFetchApp.fetch(url, {\n      method: 'post',\n      contentType: 'application/json',\n      payload: JSON.stringify(obj),\n      muteHttpExceptions: true,\n    });\n    // Optional: log for debugging\n    console.log('POST', url, res.getResponseCode(), safeTrunc_(res.getContentText(), 500));\n  } catch (err) {\n    console.error('postJson_ error:', url, err && err.message);\n  }\n}\n\n/**\n * UTILITIES\n */\nfunction isChecked_(val) {\n  // Accepts true/false, \"TRUE\"/\"FALSE\", 1/0\n  if (val === true) return true;\n  if (val === false) return false;\n  const s = String(val).trim().toUpperCase();\n  return s === 'TRUE' || s === '1' || s === 'CHECKED' || s === 'YES';\n}\n\nfunction asString_(v) {\n  return v == null ? '' : String(v);\n}\n\nfunction safeTrunc_(s, n) {\n  if (!s) return '';\n  return s.length > n ? s.slice(0, n) + '…' : s;\n}\n\n/**\n * OPTIONAL: create an installable onEdit trigger (runs with full auth scope)\n * Run this once if you want to avoid simple-trigger limitations.\n */\nfunction createInstallableTrigger() {\n  const ss = SpreadsheetApp.getActive();\n  // Clear existing triggers for safety\n  ScriptApp.getProjectTriggers().forEach(t => {\n    if (t.getHandlerFunction && t.getHandlerFunction() === 'onEdit') {\n      ScriptApp.deleteTrigger(t);\n    }\n  });\n  ScriptApp.newTrigger('onEdit').forSpreadsheet(ss).onEdit().create();\n}\n\n/**\n * OPTIONAL: quick test (does not fire onEdit)\n * Replace payloads and run from editor to verify webhooks.\n */\nfunction testPing_() {\n  postJson_(WEBHOOKS.STAGE_CHANGED, {\n    name: 'Test User',\n    email: 'test@example.com',\n    source_text: 'Instagram',\n    stage: 'Proposal',\n    previous_stage: 'Screening',\n    row: 2,\n    tab: TABS.LEADS,\n  });\n\n  postJson_(WEBHOOKS.LEAD_QUALIFIED, {\n    name: 'Test User',\n    email: 'test@example.com',\n    qualified: true,\n    row: 2,\n    tab: TABS.LEADS,\n  });\n\n  postJson_(WEBHOOKS.CLIENT_STATUS_CHANGED, {\n    email: 'client@example.com',\n    project_status: 'Delivered',\n    previous_status: 'In Progress',\n    row: 3,\n    tab: TABS.CLIENTS,\n  });\n}\n"
      },
      "typeVersion": 1
    },
    {
      "id": "7b60fbcd-c39a-4313-967a-e7b8310286c1",
      "name": "Nota Adhesiva2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -944,
        -688
      ],
      "parameters": {
        "color": 4,
        "width": 848,
        "height": 240,
        "content": "# Lead Qualification"
      },
      "typeVersion": 1
    },
    {
      "id": "bff994fa-80d2-4ccd-8964-72fc8a694cc3",
      "name": "Nota Adhesiva3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -944,
        -448
      ],
      "parameters": {
        "color": 4,
        "width": 848,
        "height": 256,
        "content": "# Changing Stage to Meeting Booked\n"
      },
      "typeVersion": 1
    },
    {
      "id": "ea32e27d-3fc6-4bed-a61d-57ec7aa24fc0",
      "name": "Nota Adhesiva4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -944,
        -192
      ],
      "parameters": {
        "color": 4,
        "width": 1440,
        "height": 384,
        "content": "# Proposal Follow-Up / Client Won Flow\n"
      },
      "typeVersion": 1
    },
    {
      "id": "5eb61a5b-b07c-42a7-9ff4-d178985e04b2",
      "name": "Nota Adhesiva5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -944,
        192
      ],
      "parameters": {
        "color": 4,
        "width": 1440,
        "height": 304,
        "content": "# Project Fulfillment Duration\n"
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "pinData": {},
  "settings": {
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": "{{ERROR_WORKFLOW_ID}}",
    "executionOrder": "v1"
  },
  "versionId": "fbf5f5f3-e31e-4f4d-a480-f8c382bdfe04",
  "connections": {
    "12c036dc-4e13-4bfd-95d2-48da0e8337b6": {
      "main": [
        [
          {
            "node": "00ef748d-22e8-4436-a0e7-7ef1ed9017fe",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "baa49e6a-3c2e-4c42-ae96-9b2f0e5a0db3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1f2a3e16-70c7-402f-ace7-782f9de34fd3": {
      "main": [
        [
          {
            "node": "1aec66f5-ce82-4375-9202-a3ba2a5ad118",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "01223e18-22c3-4622-ba66-0425aad4bcd6": {
      "main": [
        [
          {
            "node": "438ffb35-490f-49d1-9031-c32447b59992",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "e5fe57ce-c457-4f03-9c96-7ac385b2a42f": {
      "main": [
        [
          {
            "node": "04851d87-b2c6-4383-9687-382f3bee1e7b",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "baa49e6a-3c2e-4c42-ae96-9b2f0e5a0db3": {
      "main": [
        [
          {
            "node": "855b796a-8588-4f43-8b39-48406c37c67d",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "1579d825-5a67-4e52-b7d5-f4dc493cf790": {
      "main": [
        [
          {
            "node": "1f2a3e16-70c7-402f-ace7-782f9de34fd3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "a91971ec-9414-4d0d-98d9-273761071c30": {
      "main": [
        [
          {
            "node": "12c036dc-4e13-4bfd-95d2-48da0e8337b6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "855b796a-8588-4f43-8b39-48406c37c67d": {
      "main": [
        [
          {
            "node": "25f0b4f3-0305-4ce6-83f8-e37fc90d51ca",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ec815439-bfb1-4edb-8a70-9db1c167a868": {
      "main": [
        [
          {
            "node": "99f6f896-ac20-454b-a480-88a71aabe0b0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "100c9018-23b7-4724-93ef-373566ae9c42": {
      "main": [
        [
          {
            "node": "e5fe57ce-c457-4f03-9c96-7ac385b2a42f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "438ffb35-490f-49d1-9031-c32447b59992": {
      "main": [
        [
          {
            "node": "1579d825-5a67-4e52-b7d5-f4dc493cf790",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "c0760f3c-0feb-43bb-9b52-cc9307e0b98f": {
      "main": [
        [
          {
            "node": "a91971ec-9414-4d0d-98d9-273761071c30",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b2acf148-b6bc-49d3-8e76-fe890459c1dc": {
      "main": [
        [
          {
            "node": "01223e18-22c3-4622-ba66-0425aad4bcd6",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Preguntas frecuentes

¿Cómo usar este flujo de trabajo?

Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.

¿En qué escenarios es adecuado este flujo de trabajo?

Avanzado - CRM, IA Multimodal

¿Es de pago?

Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.

Información del flujo de trabajo
Nivel de dificultad
Avanzado
Número de nodos24
Categoría2
Tipos de nodos8
Descripción de la dificultad

Adecuado para usuarios avanzados, flujos de trabajo complejos con 16+ nodos

Autor
Ziad Adel

Ziad Adel

@ziadadel

AI Automation Growth Partner with 5 years of experience in the tech and hyper growing startups industry

Enlaces externos
Ver en n8n.io

Compartir este flujo de trabajo

Categorías

Categorías: 34