Перейти к содержанию

Интеграция HubSpot и n8n: контакты, сделки и дедупликация по email/phone

Обновлено: 2026-05-30

AI summary: Problem/Solution-гайд по HubSpot и n8n: как передавать лиды, обновлять contact и deal, сохранять associations и не плодить дубли в CRM.
Готовый blueprint для внедрения

Импортируйте JSON в n8n, замените credentials, URL API, project/list IDs, поля и лимиты под вашу инфраструктуру.

Проблема: HubSpot даёт мощную CRM-модель, но прямой create contact/create deal из n8n без поиска дублей создаёт разрозненные контакты и сделки без associations.

Решение: Нужно использовать n8n как слой качества данных: нормализовать email/phone, искать contact, создавать или обновлять его, затем создавать deal и association только по явному бизнес-правилу.

Схема интеграции HubSpot и n8n для контактов, сделок и дедупликации
Схема показывает, как n8n ищет contact, обновляет properties и связывает deal с CRM-объектами.

Проблема: почему простая интеграция создаёт дубли и ручной хаос

HubSpot часто подключают к формам, лендингам, чат-ботам и платежам. Данные выглядят простыми: имя, email, телефон, источник. Но в CRM важна модель объектов: contact, company, deal, associations, pipeline, stage и properties.

Если workflow создаёт контакт и сделку без проверки, отдел продаж получает несколько карточек одного человека, маркетинг теряет атрибуцию, а автоматизация email nurture срабатывает не на тот lifecycle stage. Эта страница закрывает задачу интегратора: не просто отправить лид в HubSpot, а сохранить CRM-структуру.

Архитектура workflow для n8n

БлокЗадачаProduction-проверка
Webhook / lead sourceпринимает форму, чат или событие оплатыsource, event_id, email/phone
Normalize contactчистит email, phone, name и UTMвалидный email или phone
Search HubSpot contactищет существующий contact по email/phoneпоиск до create
Upsert contactобновляет properties или создаёт contactlifecycle stage и source
Create/associate dealсоздаёт deal и связывает с contactpipeline, dealstage, association
Respond / auditвозвращает статус и пишет журналбез токенов и PII в alert

Для HubSpot важно отдельно думать про contact и deal. Повторная заявка может обновлять contact, но создавать новую deal только при новом order_id, request_id или бизнес-событии.

Контракт входных данных

{
  "event_id": "lead-8842",
  "email": "maria@example.com",
  "phone": "+7 (925) 600-10-20",
  "firstname": "Мария",
  "lastname": "Иванова",
  "company": "Acme RU",
  "deal_name": "Внедрение n8n для отдела продаж",
  "amount": 240000,
  "pipeline": "default",
  "dealstage": "appointmentscheduled",
  "utm_source": "google",
  "utm_medium": "cpc",
  "utm_campaign": "hubspot_n8n",
  "source_url": "https://example.ru/hubspot"
}

Минимальный контракт — email или phone плюс event_id. Для deal нужен отдельный ключ события: иначе каждое обновление contact может случайно создать новую сделку.

Code Node: нормализация, mapping и guard-условия

const src = $json.body ?? $json;
const email = String(src.email ?? '').trim().toLowerCase();
const rawPhone = String(src.phone ?? '').trim();
let digits = rawPhone.replace(/\D/g, '');
if (digits.length === 11 && digits.startsWith('8')) digits = `7${digits.slice(1)}`;
if (digits.length === 10) digits = `7${digits}`;
if (!email && !digits) throw new Error('HubSpot contact needs email or phone');
const eventId = String(src.event_id ?? src.order_id ?? '').trim();
if (!eventId) throw new Error('No event_id for HubSpot sync');
const contactKey = email || `phone:+${digits}`;
const dealKey = `hubspot-deal:${eventId}`;
return [{ json: {
  contact_key: contactKey,
  deal_key: dealKey,
  contact_properties: {
    email,
    phone: digits ? `+${digits}` : '',
    firstname: String(src.firstname ?? src.name ?? '').trim(),
    lastname: String(src.lastname ?? '').trim(),
    company: String(src.company ?? '').trim(),
    lifecyclestage: 'lead',
    hs_analytics_source: String(src.utm_source ?? '').trim()
  },
  deal_properties: {
    dealname: String(src.deal_name ?? `Заявка ${eventId}`).trim(),
    amount: Number(src.amount ?? 0),
    pipeline: String(src.pipeline ?? 'default').trim(),
    dealstage: String(src.dealstage ?? '').trim(),
    description: `Source: ${src.source_url ?? ''}; campaign: ${src.utm_campaign ?? ''}`
  },
  idempotency_key: `hubspot:${eventId}`
}}];
Почему contact и deal нельзя считать одним объектом

Contact описывает человека, а deal — конкретную возможность или заказ. Повторная форма может обновлять контакт, но новая сделка должна создаваться только при новом бизнес-событии.

Готовый workflow JSON: скачать и импортировать

Скачать готовый workflow JSON Скачать тестовый payload

{
  "name": "Nodbot - HubSpot contact and deal sync with dedupe",
  "nodes": [
    {
      "name": "HubSpot Lead Webhook",
      "type": "n8n-nodes-base.webhook",
      "purpose": "Принять lead event"
    },
    {
      "name": "Normalize HubSpot fields",
      "type": "n8n-nodes-base.code",
      "purpose": "Собрать contact/deal properties"
    },
    {
      "name": "Search HubSpot contact",
      "type": "n8n-nodes-base.httpRequest",
      "purpose": "Найти контакт"
    },
    {
      "name": "Upsert contact",
      "type": "n8n-nodes-base.httpRequest",
      "purpose": "Создать или обновить contact"
    },
    {
      "name": "Create and associate deal",
      "type": "n8n-nodes-base.httpRequest",
      "purpose": "Создать deal и association"
    },
    {
      "name": "Respond",
      "type": "n8n-nodes-base.respondToWebhook",
      "purpose": "Вернуть статус"
    }
  ],
  "connections": "HubSpot Lead Webhook → Normalize HubSpot fields → Search HubSpot contact → Upsert contact → Create and associate deal → Respond"
}

Пошаговая настройка связки

  1. Создайте private app token HubSpot с минимальными CRM scopes.
  2. Импортируйте workflow JSON и замените portal-specific properties.
  3. Определите правило: когда обновлять contact, а когда создавать новую deal.
  4. Проверьте pipeline и dealstage на тестовом портале.
  5. Отправьте два одинаковых payload и проверьте дубли contact/deal.

Тесты перед production

curl -X POST "https://YOUR-N8N-DOMAIN/webhook/integration-hubspot-n8n-contact-deal-sync" \
  -H "Content-Type: application/json" \
  --data @integration-hubspot-n8n-contact-deal-sync-payload.json
  1. Повторный payload не создаёт дубль и возвращает тот же output key.
  2. Некорректный mapping останавливается до запроса к внешнему API.
  3. Пустые необязательные поля не ломают workflow.
  4. Ошибка API уходит в alert или DLQ с безопасным payload.
  5. Execution data не содержит секретов, токенов и лишних персональных данных.

Production-риски

  • Deal создаётся на каждое обновление. CRM получает фальшивую воронку.
  • Associations забыты. Сделка есть, но не связана с contact/company.
  • Поиск только по email. В части российских лидов email пустой или технический.
  • Неверный lifecycle stage. Маркетинговые сценарии запускаются не по тому сегменту.
  • Слишком широкий token. Private app должен иметь минимально нужные CRM-разрешения.
Карточка результата HubSpot sync с contact, deal и association
Пример результата: контакт обновлён, сделка создана и связана с нужным объектом.

Критерии готовности

  1. Повторная заявка не создаёт второй contact.
  2. Deal создаётся только по новому event_id или order_id.
  3. Contact и deal связаны association.
  4. Pipeline/dealstage протестированы на портале.
  5. Ошибки HubSpot API уходят в alert или DLQ.
Нужно подключить HubSpot без хаоса в CRM?

Nodbot настроит HubSpot + n8n: дедупликацию контактов, deal rules, associations, UTM, alert, retry и тесты на реальных payload.

Обсудить HubSpot-интеграцию