AutomationFlowsEmail & Gmail › Validate and Create Ledgers Contacts From Google Sheets with Error Handling

Validate and Create Ledgers Contacts From Google Sheets with Error Handling

ByLEDGERS @ledgers on n8n.io

Before Using this Template first search LEDGERS in the nodes list and Update!

Event trigger★★★★☆ complexity14 nodesGoogle Sheets Trigger@Ledgers/N8N Nodes Ledgers CloudGmailGoogle Sheets
Email & Gmail Trigger: Event Nodes: 14 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #4961 — we link there as the canonical source.

This workflow follows the Gmail → 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
{
  "id": "LWUeLMrYSdCmTIl2",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "My workflow",
  "tags": [],
  "nodes": [
    {
      "id": "5286306a-9e2c-4e3c-9f3c-9d4ef7820c0c",
      "name": "Google Sheets Trigger",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1920008147,
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1m4lbSUudsxNI-thsPBZ1sjMU2yWLSQf0MrURDjBwIlg",
          "cachedResultUrl": "",
          "cachedResultName": ""
        }
      },
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "40bd5635-03d9-40fa-9f14-c8a724838c7f",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        900,
        -120
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "ff50f240-ed69-4561-9831-a08fca231788",
      "name": "Replace Me",
      "type": "n8n-nodes-base.noOp",
      "position": [
        920,
        140
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "dd49af1e-0bc6-4667-a59a-ff43c2adfc84",
      "name": "LEDGERS",
      "type": "@ledgers/n8n-nodes-ledgers-cloud.ledgers",
      "position": [
        1340,
        -140
      ],
      "parameters": {
        "contactName": "={{ $json['Name'] }}",
        "additionalFields": {
          "email": "={{ $json['Email'] }}",
          "mobile": "={{ $json['Mobile'] }}"
        }
      },
      "credentials": {
        "ledgersApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "08263687-a6f4-4008-95ef-b625119d475d",
      "name": "Contact Name Error Mail Trigger",
      "type": "n8n-nodes-base.gmail",
      "position": [
        0,
        220
      ],
      "parameters": {
        "sendTo": "",
        "message": "=<p>Contact Name is Missing<br>\nrow no: {{ $json['row_number'] }}",
        "options": {},
        "subject": "Contact Creation Failure"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": false,
      "typeVersion": 2.1
    },
    {
      "id": "8d02c452-50d4-4626-ac4e-38db7796e15c",
      "name": "Contact Name Validation",
      "type": "n8n-nodes-base.if",
      "position": [
        220,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2af93c46-69d5-468c-a73c-ebf9ee39d28b",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json['Name'] }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f5d57159-4ecc-422f-a7c2-74c96c921d6a",
      "name": "Email & Mobile Format Checker",
      "type": "n8n-nodes-base.code",
      "position": [
        440,
        -100
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const email = String($json.Email || '');\nconst regex_email = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\nconst mobile = String($json.Mobile || '');\nconst regex_mobile = /^(\\+\\d{1,3}[- ]?)?\\d{10}$/;\n\nlet validEmail = false;\n\nif (!email) {\n  validEmail = true;  // Treat empty as valid\n} else if (regex_email.test(email)) {\n  validEmail = true;  // Matches regex\n} else {\n  validEmail = false; // Invalid format\n}\n\nlet validMobile = false;\n\nif (!mobile) {\n  validMobile = true;  // Treat empty as valid\n} else if (regex_mobile.test(mobile)) {\n  validMobile = true;  // Matches regex\n} else {\n  validMobile = false; // Invalid format\n}\n\nreturn {\n  json: {\n    ...$json,\n    validEmail: validEmail,\n    validMobile:validMobile\n  }\n};"
      },
      "notesInFlow": false,
      "typeVersion": 2
    },
    {
      "id": "51a32d91-3d6a-4a3d-922d-28a681579b02",
      "name": "Email & Mobile Validator",
      "type": "n8n-nodes-base.if",
      "position": [
        660,
        -100
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c3ce425a-a354-4aad-8edd-3b483c135f8b",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json[validEmail] }}",
              "rightValue": ""
            },
            {
              "id": "5728ac2b-d0c0-47a8-963e-5db64fe81429",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json[validMobile] }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "b7d48ba7-6b06-495d-be50-ecc6bf2ef0fb",
      "name": "Email/Mobile Error Mail Trigger",
      "type": "n8n-nodes-base.gmail",
      "position": [
        440,
        120
      ],
      "parameters": {
        "sendTo": "",
        "message": "=<p>Enter Valid Email/Mobile for contact: <b>{{ $json['Name'] }}</b></p>",
        "options": {},
        "subject": "Contact Creation Failure"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "notesInFlow": true,
      "typeVersion": 2.1
    },
    {
      "id": "188e5335-f3cf-4044-bd7a-804c393e53df",
      "name": "Mobile Formatter",
      "type": "n8n-nodes-base.code",
      "position": [
        1120,
        -140
      ],
      "parameters": {
        "jsCode": "const mobileRaw = String($json.Mobile || '').trim();\n\nlet mobile_country_code = '';\nlet mobile = '';\n\nif (mobileRaw) {\n  if (mobileRaw.includes('-')) {\n    // Split by -\n    const parts = mobileRaw.split('-').map(p => p.trim());\n\n    if (parts.length >= 2) {\n      mobile_country_code = parts[0];\n      mobile = parts.slice(1).join(' ');\n\n      // Ensure country code starts with +\n      if (!mobile_country_code.startsWith('+')) {\n        mobile_country_code = '+' + mobile_country_code;\n      }\n    }\n  } else {\n    // No dash, treat as mobile\n    mobile = mobileRaw;\n  }\n}\n\nreturn {\n  json: {\n    mobile_country_code,\n    mobile\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "9a91c0ae-dddb-4a56-b956-50c357c1a279",
      "name": "Contact Creation Failure Mail Trigger",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1160,
        120
      ],
      "parameters": {
        "sendTo": "",
        "message": "=Error while creating a contact from the row {{ $json['row_number'] }}\nError: {{ $json['errorMessage'] }}",
        "options": {},
        "subject": "Contact Creation Failure",
        "emailType": "text"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "319837cb-0799-4b3b-a528-80ede79b2a90",
      "name": "Contact Creation Validator",
      "type": "n8n-nodes-base.if",
      "position": [
        1560,
        -140
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2da856b9-b6ec-4630-b985-3c6c30a06b76",
              "operator": {
                "type": "string",
                "operation": "notExists",
                "singleValue": true
              },
              "leftValue": "={{ $json['errorCode'] }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "9fd8b8c5-d9bb-4cf6-a38f-7dc9e5ff366b",
      "name": "Get Created Time",
      "type": "n8n-nodes-base.code",
      "position": [
        1780,
        -160
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const date = new Date();\nconst formatted = date.getDate().toString().padStart(2, '0') + '-' +\n  (date.getMonth() + 1).toString().padStart(2, '0') + '-' +\n  date.getFullYear() + ' ' +\n  date.getHours().toString().padStart(2, '0') + ':' +\n  date.getMinutes().toString().padStart(2, '0') + ':' +\n  date.getSeconds().toString().padStart(2, '0');\n\nreturn {\n  json: {\n    ...$json,\n    created_at: formatted\n  }\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "87d9c064-ade5-451a-9290-8acfc62f123c",
      "name": "Update Sheet Contact Creation",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1800,
        120
      ],
      "parameters": {
        "columns": {
          "value": {
            "Time": "={{ $json['created_at'] }}",
            "Contact ID": "={{ $json['contact_id'] }}"
          },
          "schema": [
            {
              "id": "Time",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Contact ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Contact ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Contact ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1b1bxz-j9tgUtsl-_1kkoEs30WyF5r3xQ2fd49JmHTwo",
          "cachedResultUrl": "",
          "cachedResultName": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "d3b1397a-d068-4fe1-95af-6777351d3cbf",
  "connections": {
    "LEDGERS": {
      "main": [
        [
          {
            "node": "Contact Creation Validator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Replace Me": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "Mobile Formatter",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Replace Me",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Created Time": {
      "main": [
        [
          {
            "node": "Update Sheet Contact Creation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mobile Formatter": {
      "main": [
        [
          {
            "node": "LEDGERS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Sheets Trigger": {
      "main": [
        [
          {
            "node": "Contact Name Validation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Contact Name Validation": {
      "main": [
        [
          {
            "node": "Email & Mobile Format Checker",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Contact Name Error Mail Trigger",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email & Mobile Validator": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Email/Mobile Error Mail Trigger",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Contact Creation Validator": {
      "main": [
        [
          {
            "node": "Get Created Time",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Contact Creation Failure Mail Trigger",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email & Mobile Format Checker": {
      "main": [
        [
          {
            "node": "Email & Mobile Validator",
            "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

Before Using this Template first search LEDGERS in the nodes list and Update!

Source: https://n8n.io/workflows/4961/ — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

Automatically processes new orders added to Google Sheets. Small orders are approved instantly; large orders trigger an HTML email with one-click Approve / Reject links — each handled by an independen

Google Sheets Trigger, Google Sheets, Gmail +1
Email & Gmail

General use cases include: Property managers who manage multiple buildings or units. Building owners looking to centralize tenant repair communication. Automation builders who want to learn multi-trig

Google Sheets, Google Drive, Gmail +1
Email & Gmail

FlujoDeReservasUpdated. Uses googleSheetsTrigger, googleSheets, googleCalendar, gmail. Event-driven trigger; 27 nodes.

Google Sheets Trigger, Google Sheets, Google Calendar +1
Email & Gmail

This workflow automates the full offer letter lifecycle, from generation to final candidate response tracking. When a new row with a Pending status is added to Google Sheets, it creates a personalized

Google Sheets Trigger, Google Drive, Google Docs +2
Email & Gmail

Hiring teams often struggle with document follow-ups, offer letter generation, and stakeholder communication. Manual checks, email back-and-forth, and missing files slow down hiring and create chaos d

Google Sheets Trigger, HTTP Request, Slack +3