Reactive CRM Многоконтрактный приём лидов с сайта ← Руководство по интеграции

Многоконтрактный приём лидов с сайта

Этот документ описывает единый API-контракт, через который бэкенд сайта отправляет данные лида, контактные данные посетителя и маркетинговые UTM-метрики в ReactiveCRM одним запросом.

1. Назначение

Интегратору сайта не нужно отдельно создавать контакт, телефон, email, лид и маркетинговое событие. Один запрос должен атомарно создать:

  1. контакт;
  2. основной телефон контакта;
  3. основной email контакта, если указан;
  4. лид;
  5. связь между контактом и лидом;
  6. маркетинговое событие и UTM-атрибуцию.

Если какой-либо шаг не выполнится, весь запрос откатывается, и частично созданные данные не сохраняются.

2. Эндпоинт

http
POST /api/site/leads
Authorization: Bearer <CRM_ACCESS_TOKEN>
Content-Type: application/json

Токен должен передаваться только с бэкенда сайта или из serverless-функции. Никогда не помещайте CRM-токен в JavaScript в браузере.

Тенант определяется из токена авторизации. Все переданные UUID проверяются в рамках этого тенанта.

3. Полный JSON-контракт

json
{
  "externalId": "site-form-01J7K9A2M4Y5T6",
  "lead": {
    "leadSourceId": "7b33b18f-55bd-48a5-a8f0-e450a56dde47",
    "quality": "WARM",
    "message": "Хочу проконсультироваться по внедрению CRM"
  },
  "contact": {
    "firstName": "Ivan",
    "lastName": "Ivanov",
    "phone": "+79991234567",
    "email": "ivan@example.com"
  },
  "marketing": {
    "occurredAt": "2026-09-06T18:00:00Z",
    "visitorId": "visitor-8b7f",
    "sessionId": "session-31ac",
    "source": "website",
    "channel": "paid",
    "utmSource": "google",
    "utmMedium": "cpc",
    "utmCampaign": "summer-consulting",
    "utmContent": "banner-a",
    "utmTerm": "crm consultation",
    "gclid": "EAIaIQobChMI-example",
    "fbclid": null,
    "landingUrl": "https://example.com/consultation",
    "referrer": "https://www.google.com/"
  }
}

4. Поля запроса

4.1. Корневые поля

ПолеТипОбязательноОписание
externalIdstringрекомендуетсяСтабильный уникальный ID отправки формы для идемпотентности
leadobjectдаДанные создаваемого лида
contactobjectдаДанные контактного лица
marketingobjectнетUTM-метрики и технические данные маркетингового визита

externalId генерируется до первой попытки отправки. При повторе запроса после тайм-аута или сетевой ошибки используйте то же значение.

4.2. Объект lead

ПолеТипОбязательноОписание
leadSourceIduuidдаID источника лидов в текущем тенанте
qualitystringнетНачальная оценка: HOT, WARM, COOL или COLD
messagestringнетСообщение посетителя или комментарий к заявке

Если сайт не выполняет предварительную квалификацию, поле quality лучше не отправлять.

4.3. Объект contact

ПолеТипОбязательноОписание
firstNamestringдаИмя контактного лица
lastNamestringнетФамилия контактного лица
phonestringдаОсновной телефон; рекомендуется формат E.164
emailstring(email)нетОсновной email контактного лица

Минимальный набор данных для обработки заявки — firstName и phone.

4.4. Объект marketing

ПолеТипОбязательноОписание
occurredAtdate-timeнетВремя отправки формы; при отсутствии используется время CRM
visitorIdstringнетАнонимный ID посетителя
sessionIdstringнетID сессии сайта
sourcestringнетИсточник события, по умолчанию website
channelstringнетКанал: например, paid, organic, social, email, direct
utmSourcestringнетзначение utm_source
utmMediumstringнетзначение utm_medium
utmCampaignstringнетзначение utm_campaign
utmContentstringнетзначение utm_content
utmTermstringнетзначение utm_term
gclidstringнетИдентификатор клика Google
fbclidstringнетИдентификатор клика Meta/Facebook
landingUrlstringнетURL посадочной страницы
referrerstringнетURL предыдущей страницы

Персональные данные не должны дублироваться в объекте marketing. Имя, телефон, email и текст сообщения передаются только в contact и lead.

5. Минимальный запрос

json
{
  "externalId": "site-form-550e8400-e29b-41d4-a716-446655440000",
  "lead": {
    "leadSourceId": "7b33b18f-55bd-48a5-a8f0-e450a56dde47"
  },
  "contact": {
    "firstName": "Ivan",
    "phone": "+79991234567"
  }
}

6. Успешный ответ

Первый запрос — 201 Created

json
{
  "leadId": "22222222-2222-2222-2222-222222222222",
  "contactId": "33333333-3333-3333-3333-333333333333",
  "phoneId": "44444444-4444-4444-4444-444444444444",
  "emailId": "55555555-5555-5555-5555-555555555555",
  "marketingEventId": "66666666-6666-6666-6666-666666666666",
  "duplicate": false,
  "createdAt": "2026-09-06T18:00:01Z"
}

Если email или маркетинговые данные не переданы, соответствующие ID возвращаются как null.

Повтор с тем же externalId — 200 OK

json
{
  "leadId": "22222222-2222-2222-2222-222222222222",
  "contactId": "33333333-3333-3333-3333-333333333333",
  "phoneId": "44444444-4444-4444-4444-444444444444",
  "emailId": "55555555-5555-5555-5555-555555555555",
  "marketingEventId": "66666666-6666-6666-6666-666666666666",
  "duplicate": true,
  "createdAt": "2026-09-06T18:00:01Z"
}

Повторный запрос не должен создавать второй лид или контакт.

7. Пример cURL

bash
curl -X POST "$CRM_BASE_URL/api/site/leads" \
  -H "Authorization: Bearer $CRM_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "site-form-01J7K9A2M4Y5T6",
    "lead": {
      "leadSourceId": "7b33b18f-55bd-48a5-a8f0-e450a56dde47",
      "quality": "WARM",
      "message": "Перезвоните мне"
    },
    "contact": {
      "firstName": "Ivan",
      "lastName": "Ivanov",
      "phone": "+79991234567",
      "email": "ivan@example.com"
    },
    "marketing": {
      "utmSource": "google",
      "utmMedium": "cpc",
      "utmCampaign": "crm-demo",
      "landingUrl": "https://example.com/demo"
    }
  }'

8. Интерфейсы TypeScript

ts
type LeadQuality = 'HOT' | 'WARM' | 'COOL' | 'COLD';

interface SiteLeadRequest {
  externalId?: string;
  lead: {
    leadSourceId: string;
    quality?: LeadQuality;
    message?: string;
  };
  contact: {
    firstName: string;
    lastName?: string;
    phone: string;
    email?: string;
  };
  marketing?: {
    occurredAt?: string;
    visitorId?: string;
    sessionId?: string;
    source?: string;
    channel?: string;
    utmSource?: string;
    utmMedium?: string;
    utmCampaign?: string;
    utmContent?: string;
    utmTerm?: string;
    gclid?: string;
    fbclid?: string;
    landingUrl?: string;
    referrer?: string;
  };
}

interface SiteLeadResponse {
  leadId: string;
  contactId: string;
  phoneId: string;
  emailId: string | null;
  marketingEventId: string | null;
  duplicate: boolean;
  createdAt: string;
}

9. Пример на стороне сервера

ts
export async function sendLeadToCrm(payload: SiteLeadRequest): Promise<SiteLeadResponse> {
  const response = await fetch(`${process.env.CRM_BASE_URL}/api/site/leads`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.CRM_ACCESS_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    const body = await response.text();
    throw new Error(`ReactiveCRM returned ${response.status}: ${body}`);
  }

  return response.json() as Promise<SiteLeadResponse>;
}

10. Ошибки

HTTPПричинаДействие интегратора
400Ошибка формата, отсутствует обязательное поле, некорректный email/телефон/qualityИсправьте данные; не повторяйте автоматически без изменений
401Токен отсутствует или недействителенОбновите токен интеграции
403Нет доступа к тенанту или операцииПроверьте пользователя и роль интеграции
404leadSourceId не найден в текущем тенантеОбновите ID источника в настройках сайта
409externalId уже связан с несовместимым запросомПроверьте генерацию ID и журнал интеграции
5xxВременная ошибка CRMПовторите тот же запрос с тем же externalId

Пример ошибки валидации:

json
{
  "status": 400,
  "error": "Bad Request",
  "message": "contact.phone must not be blank",
  "path": "/api/site/leads"
}

11. Идемпотентность

12. Мини-инструкция для разработчика сайта

  1. При первом визите сохраните UTM-метки, gclid, fbclid, URL посадочной страницы и referrer.
  2. При отправке формы сгенерируйте стабильный externalId.
  3. Отправьте форму на бэкенд сайта.
  4. Бэкенд добавляет CRM-токен и вызывает POST /api/site/leads.
  5. При 201 или 200 с duplicate: true считайте заявку доставленной.
  6. При тайм-ауте или 5xx повторите запрос с тем же externalId.
  7. Никогда не отправляйте CRM-токен напрямую из браузера.

13. Что получает менеджер CRM

После успешного запроса в CRM появляется лид, который:

Этого набора достаточно, чтобы менеджер увидел заявку, позвонил контакту и квалифицировал лид.