{
  "name": "Nodbot - n8n Google Sheets upsert by phone",
  "nodes": [
    {
      "parameters": {
        "content": "Google Sheets upsert по телефону\n\n1. Импортируйте workflow в n8n.\n2. В ноде Google Sheets выберите credential, spreadsheetId и лист Leads.\n3. Убедитесь, что в таблице есть колонка phone_normalized.\n4. Отправьте тестовый payload дважды: второй запуск должен обновить строку, а не создать дубль.",
        "height": 260,
        "width": 460
      },
      "id": "a1111111-1111-4111-8111-111111111111",
      "name": "How to use this template",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -720,
        -260
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "google-sheets-upsert-by-phone",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "b2222222-2222-4222-8222-222222222222",
      "name": "Webhook input",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -720,
        40
      ],
      "webhookId": "google-sheets-upsert-by-phone"
    },
    {
      "parameters": {
        "jsCode": "const input = $json.body ?? $json;\nconst raw = String(input.phone ?? '').trim();\nlet digits = raw.replace(/\\D/g, '');\n\nif (digits.length === 11 && digits.startsWith('8')) {\n  digits = `7${digits.slice(1)}`;\n}\nif (digits.length === 10) {\n  digits = `7${digits}`;\n}\nif (!/^7\\d{10}$/.test(digits)) {\n  throw new Error(`Invalid phone: ${raw}`);\n}\n\nreturn [{\n  json: {\n    phone_raw: raw,\n    phone_normalized: `+${digits}`,\n    name: input.name ?? '',\n    email: input.email ?? '',\n    source: input.source ?? 'unknown',\n    external_id: input.external_id ?? input.lead_id ?? '',\n    first_seen_at: input.received_at ?? new Date().toISOString(),\n    updated_at: new Date().toISOString(),\n    update_count: Number(input.update_count ?? 0) + 1\n  }\n}];"
      },
      "id": "c3333333-3333-4333-8333-333333333333",
      "name": "Normalize phone",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -460,
        40
      ]
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "YOUR_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Leads",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "phone_raw": "={{ $json.phone_raw }}",
            "phone_normalized": "={{ $json.phone_normalized }}",
            "name": "={{ $json.name }}",
            "email": "={{ $json.email }}",
            "source": "={{ $json.source }}",
            "external_id": "={{ $json.external_id }}",
            "first_seen_at": "={{ $json.first_seen_at }}",
            "updated_at": "={{ $json.updated_at }}",
            "update_count": "={{ $json.update_count }}"
          },
          "matchingColumns": [
            "phone_normalized"
          ],
          "schema": [
            {
              "id": "phone_raw",
              "displayName": "phone_raw",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "phone_normalized",
              "displayName": "phone_normalized",
              "required": true,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "name",
              "displayName": "name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "email",
              "displayName": "email",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "source",
              "displayName": "source",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "external_id",
              "displayName": "external_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "first_seen_at",
              "displayName": "first_seen_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "updated_at",
              "displayName": "updated_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            },
            {
              "id": "update_count",
              "displayName": "update_count",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "handlingExtraData": "ignoreIt"
        }
      },
      "id": "d4444444-4444-4444-8444-444444444444",
      "name": "Append or update lead row",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -200,
        40
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "REPLACE_WITH_CREDENTIAL_ID",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { ok: true, phone_normalized: $json.phone_normalized, message: 'lead row appended or updated' } }}",
        "options": {
          "responseCode": 200
        }
      },
      "id": "e5555555-5555-4555-8555-555555555555",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        80,
        40
      ]
    }
  ],
  "connections": {
    "Webhook input": {
      "main": [
        [
          {
            "node": "Normalize phone",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize phone": {
      "main": [
        [
          {
            "node": "Append or update lead row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append or update lead row": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "name": "nodbot"
    },
    {
      "name": "google-sheets"
    },
    {
      "name": "upsert"
    }
  ],
  "triggerCount": 0,
  "updatedAt": "2026-05-30T00:00:00.000Z",
  "versionId": "nodbot-google-sheets-upsert-by-phone-v2",
  "meta": {
    "templateCredsSetupCompleted": false
  }
}