{
  "name": "School 1 · Tilda / Taplink form → amoCRM deal with UTM + Telegram alert",
  "nodes": [
    {
      "id": "9c739aa2-0bf2-4484-ad06-7ae3e0535c79",
      "name": "About",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -620,
        -180
      ],
      "parameters": {
        "content": "## Tilda / Taplink form → amoCRM deal with UTM + Telegram alert\nDemo recipe for the portfolio, not a client project.\n\n**RU.** Заявка с формы становится сделкой в amoCRM с метками UTM, менеджер получает сообщение в Telegram со ссылкой на сделку.\n**EN.** A form submission becomes an amoCRM deal with UTM fields, and the manager gets a Telegram message with a link to the deal.\n\n**Setup**\n1. Copy the production URL of \"Form webhook\" into Tilda (Site settings → Forms → Webhook) or Taplink (form → integrations → webhook).\n2. Add hidden fields utm_source, utm_medium, utm_campaign, utm_content, utm_term to the form.\n3. Header Auth credential for amoCRM: name `Authorization`, value `Bearer <long-lived token>`. Replace YOUR_SUBDOMAIN.\n4. Set PIPELINE_ID in \"Build deal\" (0 = main pipeline).\n5. Connect a Telegram bot and set the managers' chat ID.\n\nMax Igoshev, igoshevmax@gmail.com, igoshevmax.com",
        "width": 520,
        "height": 520
      }
    },
    {
      "id": "a1d6ccdf-edd2-49e9-8314-6cd22dbd1e36",
      "name": "Form webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "e3252e95-69b5-4e54-8e63-d9bf5a089129",
      "parameters": {
        "httpMethod": "POST",
        "path": "school-form",
        "responseMode": "onReceived",
        "options": {}
      }
    },
    {
      "id": "5e42398f-506c-4d1f-925b-93fafa221c26",
      "name": "Clean fields",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        0
      ],
      "parameters": {
        "jsCode": "// Tilda sends application/x-www-form-urlencoded, Taplink sends JSON: both land in body.\nconst b = $json.body ?? $json;\n// Tilda checks a new webhook with test=test. Answer it and stop.\nif (b.test === 'test') return [];\nconst pick = (...keys) => keys.map(k => b[k]).find(v => typeof v === 'string' && v.trim())?.trim() ?? '';\nconst phoneRaw = pick('phone', 'Phone', 'tel', 'telefon');\nlet phone = phoneRaw.replace(/\\D/g, '');\nif (phone.length === 11 && phone.startsWith('8')) phone = '7' + phone.slice(1);\nreturn [{ json: {\n  name: pick('name', 'Name', 'full_name', 'fio') || 'Без имени',\n  phone: phone ? '+' + phone : '',\n  phoneDigits: phone,\n  email: pick('email', 'Email'),\n  course: pick('course', 'Course', 'tariff', 'product') || 'Курс не указан',\n  utm_source: pick('utm_source') || 'direct',\n  utm_medium: pick('utm_medium'),\n  utm_campaign: pick('utm_campaign'),\n  utm_content: pick('utm_content'),\n  utm_term: pick('utm_term'),\n  page: pick('referer', 'page', 'formname'),\n  formId: pick('formid', 'tranid'),\n} }];"
      }
    },
    {
      "id": "ac88b381-41fd-415a-8430-85f12ce4c391",
      "name": "Find contact by phone",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        480,
        0
      ],
      "parameters": {
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "method": "GET",
        "url": "https://YOUR_SUBDOMAIN.amocrm.ru/api/v4/contacts",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "={{ $json.phoneDigits || $json.email }}"
            }
          ]
        }
      },
      "alwaysOutputData": true
    },
    {
      "id": "45e41d5c-93f1-479a-b668-6d18be0489a0",
      "name": "Build deal",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        720,
        0
      ],
      "parameters": {
        "jsCode": "// amoCRM answers 204 with an empty body when nothing is found.\nconst lead = $('Clean fields').first().json;\nconst found = (lead.phoneDigits || lead.email) ? ($json?._embedded?.contacts?.[0]?.id ?? null) : null;\nconst PIPELINE_ID = 0; // 0 = main pipeline\nconst utm = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term']\n  .filter(k => lead[k])\n  .map(k => ({ field_code: k.toUpperCase(), values: [{ value: lead[k] }] }));\nconst deal = {\n  name: 'Заявка: ' + lead.course,\n  ...(PIPELINE_ID ? { pipeline_id: PIPELINE_ID } : {}),\n  custom_fields_values: utm,\n  _embedded: { tags: [{ name: 'Сайт' }, { name: lead.utm_source }] },\n};\nconst contactFields = [];\nif (lead.phone) contactFields.push({ field_code: 'PHONE', values: [{ value: lead.phone, enum_code: 'WORK' }] });\nif (lead.email) contactFields.push({ field_code: 'EMAIL', values: [{ value: lead.email, enum_code: 'WORK' }] });\nif (found) deal._embedded.contacts = [{ id: found }];\nelse deal._embedded.contacts = [{ first_name: lead.name, custom_fields_values: contactFields }];\nreturn [{ json: { existing: Boolean(found), body: [deal] } }];"
      }
    },
    {
      "id": "36f7b4ba-3cda-44cc-acfb-9dedc6124885",
      "name": "Contact exists?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        960,
        0
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "4318a44b-423d-4df2-94e2-dcdd35ae2bd8",
              "leftValue": "={{ $json.existing }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "9be6c048-b2cd-4e72-a470-7b14e042f1da",
      "name": "Deal for existing contact",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1200,
        -120
      ],
      "parameters": {
        "options": {},
        "method": "POST",
        "url": "https://YOUR_SUBDOMAIN.amocrm.ru/api/v4/leads",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.body) }}"
      }
    },
    {
      "id": "ee8b6648-a6fe-42e2-9d1d-b75341b22675",
      "name": "Deal + new contact",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1200,
        120
      ],
      "parameters": {
        "options": {},
        "method": "POST",
        "url": "https://YOUR_SUBDOMAIN.amocrm.ru/api/v4/leads/complex",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.body) }}"
      }
    },
    {
      "id": "cde97db7-75fd-49bd-bbfa-c820e2b0bd49",
      "name": "Alert manager",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1440,
        0
      ],
      "webhookId": "8a1e78ef-74f8-480f-8561-7517fbd1e520",
      "parameters": {
        "chatId": "YOUR_MANAGERS_CHAT_ID",
        "text": "=Новая заявка: {{ $('Clean fields').first().json.course }}\n{{ $('Clean fields').first().json.name }} · {{ $('Clean fields').first().json.phone || $('Clean fields').first().json.email }}\nИсточник: {{ $('Clean fields').first().json.utm_source }}{{ $('Clean fields').first().json.utm_campaign ? ' / ' + $('Clean fields').first().json.utm_campaign : '' }}\nСделка: https://YOUR_SUBDOMAIN.amocrm.ru/leads/detail/{{ $json.id ?? $json._embedded?.leads?.[0]?.id }}",
        "additionalFields": {
          "appendAttribution": false
        }
      }
    }
  ],
  "connections": {
    "Form webhook": {
      "main": [
        [
          {
            "node": "Clean fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean fields": {
      "main": [
        [
          {
            "node": "Find contact by phone",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find contact by phone": {
      "main": [
        [
          {
            "node": "Build deal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build deal": {
      "main": [
        [
          {
            "node": "Contact exists?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Contact exists?": {
      "main": [
        [
          {
            "node": "Deal for existing contact",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Deal + new contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deal for existing contact": {
      "main": [
        [
          {
            "node": "Alert manager",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deal + new contact": {
      "main": [
        [
          {
            "node": "Alert manager",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "active": false,
  "tags": []
}
