Форма обратной связи, которая шлёт письмо сотруднику в отпуске, — кладбище заявок. В Diil каждая форма вашего сайта превращается в карточку в CRM: с телефоном или почтой, состоянием и ответственным, который перезвонит. Хватит одного атрибута на <form>, а если нужно больше контроля — есть JavaScript и отправка с сервера.
Отправить заявку можно тремя способами — выбирайте подходящий:
- Форма с
data-crm-lead— ноль JavaScript. Виджет сам перехватывает отправку и делает остальное. window.crmLead()— для React, Vue и любых форм, состоянием которых вы управляете сами.POST /marketing/leadс вашего сервера — заказы по телефону, формы на бэкенде, интеграции с другими системами.
Как заявка попадает из формы в CRM
Заявка в Diil — это особый вид события. У каждого события есть имя, например contact_form, и это имя сначала нужно завести в CRM. Отметьте у него «Это заявка» — и каждая отправка с таким именем станет карточкой в CRM → Заявки.
- Посетитель заполняет форму и жмёт «Отправить».
- Виджет собирает поля, разбирается, где имя, телефон, почта и сообщение, и отправляет.
- Сервер проверяет, что имя события заведено, в заявке есть телефон или почта и она не похожа на бота.
- В CRM появляется карточка заявки, а если настроено — ещё и уведомление в Telegram.
Два правила, которые стоит запомнить сразу:
- Незаведённое имя отклоняется. CRM принимает только то, о чём знает, — опечатки не превращаются в мусор.
- Заведено, но без отметки «Это заявка»? Тогда отправка сохранится как обычное событие, а карточки заявки не будет.
Заведите тип заявки в CRM
Пара минут кликов, один раз на каждый вид формы:
Откройте Маркетинг → События
Здесь описываются все события, которые может присылать ваш сайт.Создайте событие
Дайте ему имя, которое будет в коде: латиница, цифры и подчёркивания, первой — буква:contact_form,callback_request,quote_request.Отметьте «Это заявка»
Теперь отправки становятся карточками. Как подсказывает сама CRM, форма должна прислать способ связи — телефон или почту.По желанию: отметьте «Событие приносит деньги»
Только тогда у заявки сохранятся сумма и валюта. Без этой отметки они тихо игнорируются.
Способ A: форма с одним атрибутом
Добавьте data-crm-lead="имя_события" любой форме на странице, где подключён скрипт виджета. Это вся интеграция. Формы, которые появляются позже — в модальном окне, после перехода в одностраничном приложении, — подхватываются автоматически.
Полный пример
Форма «Запросить расчёт»: имя, телефон, почта и сообщение, сумма для отчёта по выручке, «спасибо» после отправки и своя проверка. Сначала чистый HTML; версии для React и Vue вызывают crmLead() из собственного обработчика отправки.
<form id="quote" data-crm-lead="quote_request" data-crm-value="149900" data-crm-currency="EUR">
<label>Ваше имя <input name="name" autocomplete="name" required></label>
<label>Телефон <input name="phone" type="tel" autocomplete="tel"></label>
<label>Почта <input name="email" type="email" autocomplete="email"></label>
<label>Что нужно сделать? <textarea name="message" rows="4"></textarea></label>
<!-- Не из списка известных полей: попадёт в карточку заявки как есть -->
<label>Размер команды
<select name="team_size">
<option>1–5</option>
<option>6–20</option>
<option>20+</option>
</select>
</label>
<button type="submit">Отправить</button>
<p class="form-status" role="status" hidden></p>
</form>
<script>
const form = document.getElementById('quote');
const status = form.querySelector('.form-status');
const button = form.querySelector('button');
function say(text) {
status.textContent = text;
status.hidden = false;
}
// 1. Перед отправкой: своя проверка. preventDefault() = ничего не уйдёт
form.addEventListener('crm:lead-before', (event) => {
const phone = form.elements.phone.value.trim();
const email = form.elements.email.value.trim();
if (!phone && !email) {
event.preventDefault();
say('Оставьте телефон или почту, чтобы мы могли ответить.');
return;
}
button.disabled = true;
});
// 2. После отправки: виджет ничего не показывает, «спасибо» — за вами
form.addEventListener('crm:lead', (event) => {
button.disabled = false;
say(event.detail.ok
? 'Спасибо! Ответим в течение рабочего дня.'
: 'Что-то пошло не так. Попробуйте ещё раз или позвоните нам.');
});
</script>
<script src="https://widget.sitecog.com/widget.js" defer></script>'use client';
import { useState, type FormEvent } from 'react';
const EMPTY = { name: '', phone: '', email: '', message: '' };
export function QuoteForm() {
const [form, setForm] = useState(EMPTY);
const [status, setStatus] = useState<'idle' | 'sending' | 'done' | 'error'>('idle');
const set = (key: keyof typeof EMPTY) => (e: { target: { value: string } }) =>
setForm((prev) => ({ ...prev, [key]: e.target.value }));
async function onSubmit(e: FormEvent) {
e.preventDefault();
if (!form.phone.trim() && !form.email.trim()) {
setStatus('error');
return;
}
setStatus('sending');
// crmLead не бросает исключений; undefined — только если на странице нет widget.js
const res = await window.crmLead?.('quote_request', form, { value: 149900, currency: 'EUR' });
if (res?.ok) {
setForm(EMPTY);
setStatus('done');
} else {
setStatus('error');
}
}
if (status === 'done') return <p>Спасибо! Ответим в течение рабочего дня.</p>;
// Без data-crm-lead: эта форма отправляет заявку сама, через crmLead
return (
<form onSubmit={onSubmit}>
<input value={form.name} onChange={set('name')} placeholder="Ваше имя" required />
<input value={form.phone} onChange={set('phone')} type="tel" placeholder="Телефон" />
<input value={form.email} onChange={set('email')} type="email" placeholder="Почта" />
<textarea value={form.message} onChange={set('message')} placeholder="Что нужно сделать?" />
<button disabled={status === 'sending'}>Отправить</button>
{status === 'error' && <p role="alert">Оставьте телефон или почту и попробуйте ещё раз.</p>}
</form>
);
}<script setup lang="ts">
import { reactive, ref } from 'vue';
const empty = () => ({ name: '', phone: '', email: '', message: '' });
const form = reactive(empty());
const status = ref<'idle' | 'sending' | 'done' | 'error'>('idle');
async function submit() {
if (!form.phone.trim() && !form.email.trim()) {
status.value = 'error';
return;
}
status.value = 'sending';
// crmLead не бросает исключений; undefined — только если на странице нет widget.js
const res = await window.crmLead?.('quote_request', { ...form }, { value: 149900, currency: 'EUR' });
if (res?.ok) {
Object.assign(form, empty());
status.value = 'done';
} else {
status.value = 'error';
}
}
</script>
<template>
<p v-if="status === 'done'">Спасибо! Ответим в течение рабочего дня.</p>
<!-- Без data-crm-lead: эта форма отправляет заявку сама, через crmLead -->
<form v-else @submit.prevent="submit">
<input v-model="form.name" placeholder="Ваше имя" required />
<input v-model="form.phone" type="tel" placeholder="Телефон" />
<input v-model="form.email" type="email" placeholder="Почта" />
<textarea v-model="form.message" placeholder="Что нужно сделать?" />
<button :disabled="status === 'sending'">Отправить</button>
<p v-if="status === 'error'" role="alert">Оставьте телефон или почту и попробуйте ещё раз.</p>
</form>
</template>Что происходит при отправке формы
- Виджет всегда отменяет обычную отправку — страница не перезагружается,
actionформы не используется. - Генерирует на форме событие
crm:lead-before. Если какой-нибудь обработчик вызоветevent.preventDefault(), на этом всё — ничего не отправится. - Собирает поля через
FormData— кроме исключённых: паролей, полей сdata-crm-ignoreи полей с «чувствительными» именами. Учитываются только текстовые значения: поля с файлами пропускаются. Несколько полей с одним именем (группа чекбоксов) склеиваются через", ". - Отправляет заявку под защитой одноразового пропуска формы (подробнее — в разделе о защите от спама).
- Генерирует
crm:leadс{ ok: true }или{ ok: false }вevent.detail. - Если
okравно true, вызываетform.reset(). При неудаче форма остаётся как есть — посетитель не потеряет набранное.
Своего интерфейса у виджета нет: ни всплывашки, ни спиннера, ни «спасибо». Сайт ваш, дизайн ваш, слова тоже ваши — слушайте crm:lead и показывайте что угодно.
Атрибуты формы
| Атрибут | Пример | Что делает |
|---|---|---|
data-crm-lead | "quote_request" | Превращает форму в форму заявки. Значение — имя события, заведённого в CRM. Пустой атрибут означает имя lead_submit — его тоже нужно завести. |
data-crm-value | "149900" | Сумма в минимальных единицах, целым числом: 149900 — это 1499,00. Сохраняется, только если у события отмечено «Событие приносит деньги». |
data-crm-currency | "EUR" | Валюта суммы: EUR, USD, UAH… |
data-crm-ignore | без значения | Ставится не на форму, а на поле или любую обёртку (fieldset, div…): само поле или всё, что внутри обёртки, в заявку не попадёт. См. поля, которые никогда не отправляются. |
События формы
Оба события всплывают, поэтому слушать их можно на самой форме или один раз на document — сразу для всех форм страницы.
| Событие | Когда | event.detail | Можно отменить |
|---|---|---|---|
crm:lead-before | Сразу после отправки формы, до любых запросов | { name } — имя события | Да: preventDefault() отменяет отправку |
crm:lead | После ответа сервера | { ok } — true, если заявка принята | Нет |
document.addEventListener('crm:lead', (event) => {
if (event.detail.ok) {
event.target.closest('.modal')?.classList.add('is-thanks');
}
});Какие имена полей понимает CRM
В карточке заявки четыре главных места: имя, телефон, почта и сообщение. Виджет раскладывает данные по именам полей: обрезает пробелы, переводит в нижний регистр и сверяет с этим списком точно (так что Phone сработает, а your-phone — нет):
| Место | Имена полей | Максимум символов |
|---|---|---|
| Имя | name, fio, username, user_name, fullname, full_name, firstname, first_name, contact_name, client_name, имя, фио, ім'я | 200 |
| Телефон | phone, tel, telephone, mobile, phone_number, contact_phone, телефон, тел | 40 |
| Почта | email, e_mail, e-mail, mail, contact_email, почта, пошта, емейл | 320 |
| Сообщение | message, comment, comments, text, question, note, description, task, сообщение, комментарий, вопрос, повідомлення, коментар | 4000 |
- Побеждает первое совпадение. Если в форме есть и
phone, иmobile, место займёт то поле, что идёт в форме раньше. - Остальное тоже не теряется. Незнакомые поля и повторные совпадения попадают в «дополнительные» и показываются в карточке в порядке формы. До 30 таких полей; имя — до 60 символов, значение — до 1000.
- Телефон засчитывается, только если в нём от 7 до 20 цифр. Пробелы, скобки и дефисы не мешают; плюс в начале сохраняется.
- Телефон или почта обязательны. Заявка без того и другого отклоняется с
no_contact— ответить-то некуда.
Поля, которые никогда не отправляются
Форма регистрации с паролем или оплата с номером карты тоже могут оказаться формой заявки. Чтобы такие данные случайно не осели в CRM, кое-что виджет просто не отправляет:
- Пароли — любые
<input type="password">. - Всё, что помечено
data-crm-ignore. Атрибут можно поставить на само поле или на любую обёртку —fieldset,div, что угодно: тогда пропускается всё внутри. - Поля с «чувствительными» именами. Имя разбивается на слова (по
_,-,., camelCase и так далее), и поле отбрасывается, если отдельным словом в нём стоитcard,cc,csc,cvv,cvc,iban,ssn,secretилиpass— либо если где угодно в имени встречаетсяpassword,passwd,pwd,token,csrf,xsrf,creditcard,cardnumberилиccnum.
| Имя поля | Уйдёт в заявку? | Почему |
|---|---|---|
card_number | нет | card — отдельное слово |
cvv | нет | в списке слов |
csrf_token | нет | содержит csrf и token |
user_password | нет | содержит password |
discard_reason | да | card здесь часть слова discard, а не отдельное слово |
Ещё пара деталей:
- Исключение действует на имя целиком. Если хотя бы одно поле с каким-то именем отброшено, отбрасываются все поля с этим именем.
- Скрытые поля отправляются.
<input type="hidden">— удобный способ передать товар, тариф или страницу. Если, конечно, его имя не из списка выше. - Сервер страхует. Тот же фильтр применяется и на нашей стороне — в том числе к заявкам с сервера. Так что чувствительное поле в CRM не попадёт никаким путём.
<form data-crm-lead="signup_request">
<input name="email" type="email"> <!-- уйдёт: это почта заявки -->
<input name="password" type="password"> <!-- не уйдёт: пароль -->
<fieldset data-crm-ignore> <!-- всё внутри пропускается -->
<input name="company_inn">
<input name="billing_address">
</fieldset>
<input type="hidden" name="plan" value="pro"> <!-- уйдёт: скрытое поле с обычным именем -->
<input name="card_number"> <!-- не уйдёт: card — отдельное слово -->
<button type="submit">Отправить</button>
</form>Способ B: отправка из JavaScript
Если состоянием формы управляете вы — React, Vue, многошаговый мастер, чат-бот, — вызывайте виджет напрямую. Обе функции появляются в window, как только загрузится widget.js.
crmLead(name, fields, options)
const { ok } = await window.crmLead(
'callback_request',
{ name: 'Анна', phone: '+49 30 1234567', message: 'Перезвоните после 17:00, пожалуйста' },
{ value: 149900, currency: 'EUR' },
);namestringобязательно^[A-Za-z][A-Za-z0-9_]*$). С неподходящим именем в консоли появится предупреждение, а вернётся { ok: false }.fieldsobjectобязательноpassword, card_number…) отбрасываются так же, как поля формы.options.valueintegerнеобязательно149900 = 1499,00. Сохраняется только у событий с отметкой «Событие приносит деньги».options.currencystringнеобязательноEUR.Возвращает Promise, который разрешается в:
okbooleancrmLeadForm(form, name)
Делает ровно то же, что атрибут data-crm-lead, только из кода. Пригодится, когда форму рисует сторонняя библиотека и атрибут ей не добавить или когда имя события выясняется на лету. Ничего не возвращает; результат приходит тем же событием crm:lead.
const form = document.querySelector('#newsletter-popup form');
window.crmLeadForm(form, 'newsletter_signup');
form.addEventListener('crm:lead', (e) => {
if (e.detail.ok) form.innerHTML = '<p>Готово! Загляните в почту.</p>';
});formHTMLFormElementобязательноnamestringобязательноcrmLead.Типы для TypeScript
Виджет — обычный скрипт, и TypeScript о нём ничего не знает. Добавьте это в любой файл .d.ts:
export {};
declare global {
interface Window {
crmLead?: (
name: string,
fields: Record<string, string>,
options?: { value?: number; currency?: string },
) => Promise<{ ok: boolean }>;
crmLeadForm?: (form: HTMLFormElement, name: string) => void;
}
}Как устроена защита от спама
Открытая форма притягивает ботов как магнит. Капча, чтобы их отвадить, не нужна: виджет и сервер справляются вдвоём, а живые посетители ничего не замечают.
Одноразовый пропуск формы
Перед отправкой виджет получает у сервера подписанный «пропуск формы». Чтобы не терять время, он запрашивает его, как только посетитель впервые ставит курсор в форму. Пропуск:
- одноразовый — один пропуск, одна заявка;
- привязан к вашему сайту — с другого сайта он бесполезен;
- действует 24 часа.
Боты, которые вслепую шлют POST на адрес приёма, пропуска не имеют и получают отказ. А двойной клик или повторная отправка с тем же пропуском вернут { ok: true }, но заявка сохранится один раз — никаких дублей от нетерпеливых пальцев.
Ловушка для ботов
То самое скрытое поле company_site. Человек его не видит, и оно остаётся пустым. Бот, который заполняет всё, что находит, выдаёт себя — и заявка отклоняется.
«Подозрительные» заявки
Некоторые заявки выглядят странно, но могут оказаться настоящими. Такие принимаются и получают в CRM пометку «подозрительная» — менеджер посмотрит на неё, прежде чем звонить:
- форму заполнили меньше чем за 3 секунды — быстрее, чем способен человек;
- форма провисела открытой больше 30 минут;
- с одного IP-адреса за час пришло больше 5 заявок.
«Спам» — это состояние, которое человек ставит в CRM вручную. Автоматически в спам ничего не попадает, так что настоящий клиент молча не потеряется.
IP-адрес в карточке заявки показывается усечённым: у IPv4 последний октет обнуляется (203.0.113.57 → 203.0.113.0), IPv6 обрезается до /48. Полный адрес мы не храним — подробнее в разделе Что мы храним и сколько.
Почему браузер получает только ok: true или false
Сообщить боту «отклонено: заполнена ловушка» — значит бесплатно научить его проходить защиту. Поэтому для браузера любой отказ выглядит одинаково: { ok: false }, без причины. Отлаживаете свою форму? Список типичных проблем покрывает все случаи, а серверный способ честно называет ошибку — он ведь защищён секретным ключом.
Под капотом: запрос из браузера
Для любопытных — собирать это вручную не придётся. Виджет получает пропуск в POST https://back.sitecog.com/marketing/fk (ответ: {"pass":"…"}), а затем отправляет заявку:
POST https://back.sitecog.com/marketing/f
Content-Type: application/json
{
"n": "contact_form",
"pass": "…",
"fields": {
"name": "Анна",
"email": "anna@example.com",
"message": "Здравствуйте!",
"company_site": ""
},
"val": 149900,
"cur": "EUR",
"u": "https://shop.example/contacts",
"vid": "…",
"sid": "…"
}В ответ приходит { "ok": true } или { "ok": false }. vid и sid — идентификаторы посетителя и сессии: благодаря им заявка связывается с рекламным каналом, из которого пришёл человек. Подробнее — в разделе События и аналитика.
Отправка заявок с сервера
Не каждая заявка начинается в браузере. Серверный способ нужен, когда:
- менеджер принимает заказ по телефону и вносит его в вашу учётную систему;
- форма обрабатывается на бэкенде (обработчик на PHP, server action в Next.js), и зависеть от виджета не хочется;
- заявки приходят из другой системы: маркетплейса, сервиса бронирования, бота.
POST https://back.sitecog.com/marketing/lead
x-event-key: sk_…
Content-Type: application/jsonСекретные ключи
Серверный способ работает по секретному ключу. Создайте его в CRM → Маркетинг → События → Секретные ключи → Выпустить ключ. Ключ выглядит как sk_ и 48 шестнадцатеричных символов; у сайта может быть до 5 активных ключей, любой можно отозвать. Называйте ключи по месту использования («платёжный сервер», «заказы по телефону») — когда придёт время отзывать, вы себе спасибо скажете.
Пример запроса
curl https://back.sitecog.com/marketing/lead \
-H "x-event-key: sk_3f9a1c7e5b2d4f6a8c0e1b3d5f7a9c2e4b6d8f0a1c3e5b7d" \
-H "Content-Type: application/json" \
-d '{
"name": "phone_order",
"event_id": "order-1024",
"fields": {
"name": "Анна Шмидт",
"phone": "+49 30 1234567",
"message": "Две пары Air 3, графит. Доставка в офис."
},
"value": 149900,
"currency": "EUR",
"url": "https://shop.example/contacts"
}'// Node 18+ — fetch встроен
const res = await fetch('https://back.sitecog.com/marketing/lead', {
method: 'POST',
headers: {
'x-event-key': process.env.CRM_EVENT_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'phone_order',
event_id: `order-${order.id}`, // повтор не создаст вторую заявку
visitor_id: order.crmVid, // сохранено из браузера при оформлении, если есть
session_id: order.crmSid,
fields: {
name: order.customerName,
phone: order.phone,
message: order.comment,
delivery: order.deliveryMethod, // уйдёт в «дополнительные»
},
value: order.totalCents, // 149900 = 1499,00
currency: 'EUR',
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`CRM lead: ${res.status} ${data.message}`);
// data → { ok: true, duplicate: false }import os
import requests
res = requests.post(
"https://back.sitecog.com/marketing/lead",
headers={"x-event-key": os.environ["CRM_EVENT_KEY"]},
json={
"name": "phone_order",
"event_id": "order-1024",
"fields": {
"name": "Анна Шмидт",
"phone": "+49 30 1234567",
"message": "Две пары Air 3, графит.",
},
"value": 149900,
"currency": "EUR",
},
timeout=10,
)
res.raise_for_status()
print(res.json()) # {'ok': True, 'duplicate': False}Тело запроса
x-event-keyheaderstringобязательноsk_… из CRM. Нет ключа или он отозван → 401 invalid_key.namestringобязательноn.fieldsobjectобязательноevent_idstringнеобязательноid.visitor_idstringнеобязательноsession_idstringнеобязательноcrm_sid). С ним заявка наследует рекламный канал и UTM-метки этой сессии. Короткий вариант: sid.valueintegerнеобязательно149900 = 1499,00. Сохраняется только у событий с отметкой «Событие приносит деньги». Короткий вариант: val.currencystringнеобязательноEUR. Короткий вариант: cur.urlstringнеобязательноu.Ответ
{ "ok": true, "duplicate": false }okbooleanduplicatebooleantrue, если заявка с таким event_id уже есть. Ничего нового не сохранилось — и это нормально.В отличие от браузера, на сервере нет ни пропусков формы, ни проверок по времени: секретного ключа достаточно. А вот ловушка действует: непустое company_site в fields получит 400 rejected.
Идемпотентность: повторяйте без страха
Сеть падает в самый неподходящий момент. Дошёл запрос или нет? С event_id это неважно: просто отправьте ещё раз. Повтор вернёт {"ok":true,"duplicate":true}, и заявка не сохранится дважды.
{ "ok": true, "duplicate": true }Атрибуция: какая реклама привела заявку
Заявки из виджета связываются с посетителем автоматически. Серверная заявка о браузере ничего не знает — пока вы ей не расскажете. Виджет хранит идентификатор посетителя в localStorage под ключом crm_vid, а идентификатор сессии — под crm_sid. Передайте их на бэкенд вместе с формой или заказом, а оттуда — дальше:
const crm = {
vid: localStorage.getItem('crm_vid'),
sid: localStorage.getItem('crm_sid'),
};
await fetch('/api/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...order, crm }),
});
// …а на сервере: visitor_id: body.crm.vid, session_id: body.crm.sidС session_id заявка наследует рекламный канал и UTM-метки первого просмотра этой сессии и попадает в отчёты рядом с кампанией, которая её принесла. Подробнее — в разделе События и аналитика.
Ошибки
В браузере crmLead() и событие crm:lead сообщают только ok: false. Серверный способ отвечает кодом и полем message:
| Код | Тело | Что случилось |
|---|---|---|
| 400 | {"message":"unknown_event","name":"phone_order"} | В CRM нет события с таким именем. Заведите его в Маркетинг → События (проверьте написание и регистр). |
| 400 | {"message":"invalid_event_name"} | В имени есть что-то кроме латиницы, цифр и подчёркиваний, или оно начинается не с буквы. |
| 400 | {"message":"no_contact"} | В fields нет ни телефона (7–20 цифр), ни почты. |
| 400 | {"message":"invalid_body"} | Тело запроса — не JSON-объект нужной формы. |
| 400 | {"message":"rejected"} | Заполнено поле-ловушка company_site. |
| 401 | {"message":"invalid_key"} | x-event-key не передан, записан неверно или отозван. |
| 413 | — | Тело запроса больше 8 КБ. |
| 429 | {"message":"rate_limit_exceeded"} | Слишком много запросов за эту минуту. Дождитесь следующей. |
Запросы из браузера могут получить ещё 400 unknown_origin или 400 unknown_domain, если домен страницы не заведён в CRM как сайт. Их видно во вкладке Network, а код при этом получает ok: false.
Ограничения
| Что | Предел |
|---|---|
| Запросы с одного IP (все маркетинговые адреса вместе) | 120 в минуту |
| Запросы на сайт | 6000 в минуту |
| Пропуски формы с одного IP | 20 в минуту |
| Тело запроса | 8 КБ |
| Имя / телефон / почта / сообщение | 200 / 40 / 320 / 4000 символов |
| Дополнительные поля | до 30; имя — до 60, значение — до 1000 символов |
| Телефон | 7–20 цифр, чтобы считаться контактом |
event_id | до 128 символов |
| Секретные ключи | до 5 активных на сайт |
Окна — фиксированные минуты: после 429 дождитесь начала следующей. Живой посетитель к этим пределам даже не приблизится, а скрипт, долбящий вашу форму, — вполне.
Если что-то не так
Заявка не пришла
Пройдитесь по списку — почти всегда дело в одном из пунктов:
- Имя не заведено. В Маркетинг → События должно быть событие ровно с таким именем, с учётом регистра. Пустой
data-crm-leadозначаетlead_submit— заведите его. - Не отмечено «Это заявка». Тогда отправка сохранилась как обычное событие: ищите её в статистике событий, а не в Заявках.
- Нет ни телефона, ни почты. Или они есть, но под незнакомыми CRM именами (
your-phone,contact[email]) и уехали в «дополнительные». Переименуйте поля — см. таблицу имён. Телефон короче 7 цифр тоже не засчитывается. - Данные были в поле с файлом. Файлы пропускаются — виджет отправляет только текст.
- Настоящее поле называется
company_site. Это имя ловушки, и заявку принимают за бота. - Ваш же обработчик
crm:lead-beforeвызвалpreventDefault()— возможно, не тогда, когда вы ждали. - На этой странице нет виджета. Без
widget.jsатрибут ничего не делает, и форма отправляется по старинке (или никуда). - Мешает Content Security Policy. Если у сайта есть CSP, в ней нужны
script-src https://widget.sitecog.comиconnect-src https://back.sitecog.com. Консоль браузера скажет об этом красным. - Домен не заведён в CRM. Сайт определяется по адресу страницы (
www.отбрасывается). Наlocalhostили тестовом домене, которого нет в CRM, во вкладке Network будетunknown_domain. - Вы очень активно тестировали. 20 пропусков в минуту с одного IP людям хватает с запасом, а вот лихорадочным кликам — нет. Подождите минуту.
Не дошло одно поле
Заявка в CRM есть, а какого-то поля в ней нет? Скорее всего, виджет (или сервер) отбросил его намеренно:
- У поля «чувствительное» имя.
promo_passотбрасывается, потому чтоpassв нём — отдельное слово. Переименуйте во что-нибудь нейтральное, напримерpromo_code. Полный список — в разделе Поля, которые никогда не отправляются. - Поле или его обёртка помечены
data-crm-ignore. Проверьте всех родителей поля, а не только его самого: атрибут наfieldsetилиdivвыше по дереву тоже считается. - Это
type="password"— пароли не отправляются никогда. - Где-то в форме есть второе поле с тем же именем, и оно попало под исключение — тогда отбрасываются все поля с этим именем.
- Это поле с файлом. Файлы пропускаются в любом случае — виджет отправляет только текст.
Не видно суммы
Отметьте у типа события «Событие приносит деньги». И проверьте, что сумма — целое число в минимальных единицах: 1499.00 отправляется как 149900.
Уведомления в Telegram
Заявка, которую никто не увидел, — потерянная заявка. В настройках раздела CRM → Заявки можно подключить Telegram-бота: указать токен бота, включить оповещения, решить, присылать ли подозрительные заявки, и задать тихие часы — чтобы ночная смена ботов не будила отдел продаж.
Уведомления оформлены HTML-разметкой, а всё, что ввёл посетитель, перед отправкой экранируется. «Имя» вида <a href="…">Нажмите сюда</a> придёт обычным текстом: подсунуть ссылку в чат вашей команды или сломать оформление посетитель не сможет. Длинные поля обрезаются, чтобы роман в поле «Сообщение» не превратился в простыню, — заявка целиком всегда есть в CRM.
Удаление данных человека по запросу
«Удалите всё, что у вас обо мне есть» — обычная просьба, и ответ на неё не должен превращаться в неделю раскопок по таблицам. В CRM для этого два инструмента, код с вашей стороны не нужен.
Из заявки или чата
В карточке заявки и в карточке чата есть «Удалить данные посетителя». Перед удалением CRM показывает, что именно уйдёт:
- профиль посетителя и аналитика — просмотры страниц и события;
- чаты и вложения — сколько чатов, сообщений и файлов;
- заявки — только если отметить «Также удалить заявки этого посетителя». По умолчанию выключено: заявка часто — сделка в работе, и решать вам.
Заявка, отправленная с сервера без visitor_id, к посетителю не привязана — у неё удаляется только сама заявка.
По почте или телефону
Обычно просьба приходит письмом: «Я anna@example.com, забудьте меня». Откройте CRM → Настройки → «Запросы на удаление данных», введите почту или телефон, которые указал человек, — и CRM найдёт его заявки, чаты и аккаунт на вашем сайте. Одна кнопка удаляет всё найденное; вместе с ним можно удалить и аналитику посещений связанных посетителей.
- Права. Для удаления нужны те же права, что и на редактирование этих разделов. Менеджер, который может только смотреть чаты, увидит найденное, но удалить не сможет.
- Журнал действий. Каждое удаление записывается в журнал — без почты и телефона человека, иначе журнал хранил бы ровно то, что просили забыть.
- Без отмены. Удалено — значит удалено. Проверьте цифры, прежде чем подтверждать.