Заявки с сайта в таблицу: простая CRM без внедрения CRM
Как складывать лиды с форм в таблицу и мессенджер, не покупая CRM, что при этом с персональными данными и когда пора на нормальную систему.
Содержание
При потоке в 10-40 заявок в месяц таблица плюс уведомление в мессенджер закрывают задачу не хуже CRM и стоят ноль. Важны две вещи: таймаут на запись в 5 секунд, чтобы форма не висла, и российское хранилище, иначе получаете трансграничную передачу данных.
Раз в месяц ко мне приходят с одинаковой задачей: настроить выгрузку заявок в google таблицы, чтобы лиды с формы перестали теряться в почтовом ящике. Следом обычно идёт второй вопрос: может, сразу купить CRM и не мучиться. Отвечаю сразу: при потоке 10-40 заявок в месяц таблица плюс уведомление в мессенджер закрывают почти всю реальную потребность, а стоят один вечер работы вместо внедрения на два месяца. Ниже разберу схему целиком: как это устроено технически, что писать в строку, куда при этом деваются персональные данные и в какой момент таблица начинает мешать продавать.

Я обслуживаю сайты малого бизнеса шесть лет и повидал обе крайности. Одни героически ведут заявки в переписке с самим собой в Telegram. Другие купили CRM за 60 000 в год, завели там три сделки и больше не заходили. Таблица честно живёт посередине и в 2026 году всё ещё остаётся самым недооценённым инструментом.
Кому таблицы хватает, а кому уже нет
Таблица работает, пока выполняются условия. Их немного, и они проверяются за минуту.
- Заявок до полусотни в месяц. Верхняя граница нежёсткая, но после 60-80 строк в месяц глазами это уже не читается.
- Обрабатывает один человек, максимум двое. Три менеджера в одной таблице начинают перетирать чужие правки.
- Цикл сделки короткий: позвонили, договорились, выставили счёт. Если сделка живёт три месяца и проходит семь этапов, таблица превращается в филиал ада.
- Нет обязательной отчётности по воронке. Владелец смотрит на количество и на источник, а не строит когортный анализ.
- Повторных продаж мало или они идут не через сайт.
Совпало четыре пункта из пяти: берите таблицу и не тратьте деньги. Не совпало два и больше: сразу смотрите в сторону нормальной системы, потому что переезжать потом всё равно придётся, только с грязными данными.
Отдельно про мотивацию. Владельцы часто хотят CRM не ради воронки, а ради одной простой вещи: чтобы заявка не потерялась. Это решается не системой за 60 000 в год, а дублированием канала доставки. Письмо может уйти в спам, вебхук может отвалиться, менеджер может смахнуть уведомление. Три независимых приёмника одной заявки надёжнее любой CRM с единственным каналом.
Схема без сторонних сервисов
Типовое решение из статей в интернете выглядит так: форма шлёт данные в Zapier или Make, оттуда в таблицу. С 2022 года это плохая идея по двум причинам. Оплата зарубежных сервисов из России — отдельный квест, а данные клиентов ходят через посредника, который вам ничего не обещал.
Схема без посредников проще, чем кажется:
[форма на сайте]
|
v
[обработчик на вашем домене: mail.php]
|
|--> письмо на рабочую почту
|--> сообщение в Telegram-бота
|--> POST в веб-приложение Apps Script -> строка в таблице
|--> строка в leads.log на сервере (страховка)
Обработчик у вас уже есть, если на сайте вообще работает форма. Задача сводится к тому, чтобы добавить в него один HTTP-запрос и не сломать остальное.
Что делает обработчик
Порядок действий внутри скрипта важен. Сначала пишем в локальный лог, потом рассылаем по каналам. Если упадёт вебхук в таблицу, заявка уже сохранена на диске и её можно долить руками.
// после валидации и проверки honeypot
$lead = [
'ts' => date('c'),
'id' => bin2hex(random_bytes(8)), // ключ идемпотентности
'name' => $name,
'phone' => $phone,
'email' => $email,
'message' => $message,
'page' => $_POST['page_url'] ?? '',
'utm' => $_POST['utm'] ?? '',
'ip' => $_SERVER['REMOTE_ADDR'],
];
// 1. страховка на диске, вне docroot
file_put_contents(
'/home/c/user/site/private/leads.log',
json_encode($lead, JSON_UNESCAPED_UNICODE) . "n",
FILE_APPEND | LOCK_EX
);
// 2. вебхук в таблицу, короткий таймаут
$ch = curl_init(SHEET_WEBHOOK_URL);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($lead, JSON_UNESCAPED_UNICODE),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
CURLOPT_FOLLOWLOCATION => true, // Apps Script отвечает 302 на редирект
]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code !== 200) {
file_put_contents('/home/c/user/site/private/leads-retry.log',
json_encode($lead, JSON_UNESCAPED_UNICODE) . "n", FILE_APPEND | LOCK_EX);
}
Таймаут в 5 секунд обязателен. Без него любая задержка на стороне таблицы превращается в подвисшую форму, посетитель жмёт кнопку второй раз, и вы получаете дубль. Про CURLOPT_FOLLOWLOCATION тоже не забудьте: веб-приложения Apps Script отвечают редиректом на googleusercontent, и без этого флага вы будете видеть код 302 и думать, что всё сломано.
Приёмник на стороне таблицы
Скрипт живёт прямо в таблице: Расширения, Apps Script, дальше публикация как веб-приложение с доступом «всем, у кого есть ссылка». Ссылку никому не показываем, она играет роль ключа.
function doPost(e) {
var lock = LockService.getScriptLock();
lock.waitLock(20000); // защита от гонок при пачке заявок
try {
var d = JSON.parse(e.postData.contents);
var sh = SpreadsheetApp.getActive().getSheetByName('Заявки');
// защита от дублей по ключу идемпотентности
var ids = sh.getRange('B2:B').getValues().flat();
if (ids.indexOf(d.id) !== -1) {
return ContentService.createTextOutput('dup');
}
sh.appendRow([
new Date(d.ts), d.id, d.name, d.phone, d.email,
d.message, d.page, d.utm, d.ip, 'Новая', '', ''
]);
return ContentService.createTextOutput('ok');
} finally {
lock.releaseLock();
}
}
Двадцать строк кода, ноль подписок. Единственное, за чем нужно следить: при каждом изменении скрипта Apps Script требует создать новую версию развёртывания, иначе на боевом URL продолжает крутиться старый код. Меня это подловило дважды.
Уведомление в мессенджер добавляется тем же способом, я разбирал его отдельно в статье про интеграцию формы с Telegram-ботом. Связка получается такая: в Telegram владелец видит заявку через секунду, в таблице она лежит для истории и отчётов.
Что писать в строку
Половина смысла таблицы теряется, когда в ней три колонки: имя, телефон и время. Через месяц владелец спрашивает, откуда пришли эти люди, и ответить нечем.
| Колонка | Что кладём | Зачем |
|---|---|---|
| Дата и время | ISO-строка от сервера | Считать скорость ответа и загрузку по дням недели |
| ID | случайные 16 символов | Ключ против дублей, ссылка на строку в логе |
| Имя | как ввёл посетитель | Обращение при звонке |
| Телефон | нормализованный к +7XXXXXXXXXX | Иначе поиск дублей не работает |
| Комментарий | текст из формы | Половина квалификации лида уже здесь |
| Страница | URL, откуда отправлена форма | Видно, какая услуга реально продаёт |
| UTM | source, medium, campaign одной строкой | Разделить контекст, органику и переходы |
| Статус | выпадающий список | Новая, В работе, Счёт, Оплачено, Отказ |
| Ответственный | имя менеджера | Если их двое |
| Заметка | свободный текст | Что обсудили по телефону |
Про UTM отдельно. Метки надо тащить не из текущего URL, а из первого визита: посетитель приходит по рекламе, гуляет три дня, возвращается из закладок и отправляет форму. Если читать метки в момент отправки, вся реклама превратится в «прямые заходы». Метки складываются в cookie на первом визите и оттуда подставляются в скрытое поле формы. Механику я расписывал в материале про UTM-метки для контекста.
Скрытые поля формы, которые стоит завести сразу:
<input type="hidden" name="page_url" value="">
<input type="hidden" name="utm" value="">
<input type="hidden" name="company_website" tabindex="-1" autocomplete="off">
Последнее — honeypot. Ботов на формах в разы больше, чем людей, и без ловушки таблица за месяц зарастает мусором вида «Best SEO offer». Заполняется поле только скриптом, живой человек его не видит.
Персональные данные в облачной таблице
Здесь начинается часть, которую подрядчики обычно пропускают, а потом клиент получает запрос из Роскомнадзора и удивляется.
Имя, телефон, почта и комментарий — это персональные данные. Как только вы кладёте их в облачную таблицу, вы поручаете обработку оператору этого облака. Если сервера физически за границей, это трансграничная передача, и с 2023 года она требует отдельного уведомления в РКН до начала передачи. Просто галочки в согласии тут мало. Тему я разбирал подробно в статье про трансграничную передачу данных, пересказывать не буду, скажу главное на практике.
Что реально нужно сделать, если данные всё-таки едут в зарубежное облако:
- В согласии на обработку прямо перечислить, кому передаются данные и с какой целью.
- Подать уведомление об обработке ПДн, а для трансграничной передачи — отдельное уведомление о намерении.
- Ограничить доступ к таблице поимённо. Доступ по ссылке «всем в интернете» на таблицу с телефонами клиентов — это готовый инцидент.
- Настроить срок хранения. Заявки трёхлетней давности не нужны, а ответственность за их утечку никуда не делась.
- Не складывать в таблицу лишнее. Паспорт, дата рождения, адрес доставки в таблице с общим доступом — плохая идея в любой конфигурации.
Есть и вариант проще: держать в облаке только обезличенную часть. Дата, источник, страница, статус, сумма. Имя и телефон живут в логе на вашем сервере и в мессенджере, а в таблице лежит только ID. Отчётность по маркетингу работает полностью, а персональных данных в чужом облаке нет. Для владельца, который хочет спать спокойно и не разбираться в уведомлениях, это лучший компромисс из тех, что я пробовал.
Российские альтернативы
Когда клиент говорит «давай без иностранных сервисов», у меня есть короткий список того, что реально работает в 2026 году.
| Вариант | Как принимает заявки | Ориентир по цене |
|---|---|---|
| Яндекс Документы (таблицы) | вебхук через Яндекс Формы или свой скрипт-прослойка | бесплатно, платный бизнес-тариф от 300 руб. за пользователя в месяц |
| Р7-Офис, облако | REST API документов, макросы | от 400 руб. за пользователя в месяц |
| МойОфис Частное облако | API, разворачивается у себя | лицензия от нескольких десятков тысяч, для микробизнеса дорого |
| NocoDB или Baserow на своём VPS | штатный REST API, вебхуки | VPS от 600 руб. в месяц, софт бесплатный |
| Обычный CSV на своём хостинге | пишет тот же обработчик | 0 руб., открывается любым офисом |
Отдельно похвалю связку NocoDB на маленьком VPS: получается таблица с человеческим интерфейсом, с фильтрами и правами доступа, при этом данные лежат на вашем сервере в вашей стране. Ставится за час, ест 512 МБ памяти. Для клиента с чувствительными данными я обычно предлагаю именно это.
Самописный CSV недооценивают зря. Файл на сервере, права 600, скачивается по кнопке из закрытого раздела. Никакой облачной обвязки, никаких уведомлений о трансграничной передаче, открывается в чём угодно. Минус один: совместная работа отсутствует как класс.
Дубли и потери
Интеграция без обработки сбоев ломается тихо. Заявки перестают доходить, а узнают об этом через неделю по звонку возмущённого клиента «я вам писал, вы не ответили».
Откуда берутся потери:
- Apps Script упёрся в квоту (лимиты на количество вызовов и время исполнения никто не отменял).
- Сменили развёртывание, старый URL умер, а в mail.php остался прежний.
- Хостинг закрыл исходящие соединения после смены тарифа.
- Кончилось место на диске, и запись в лог отвалилась вместе со всем остальным.
Откуда берутся дубли: посетитель дважды жмёт кнопку, скрипт ретраит после таймаута, а также редирект Apps Script, из-за которого запрос иногда исполняется дважды. Лечится ключом идемпотентности, который я закладывал в код выше, и проверкой по колонке B.
Дозаливка потерянного делается кроном раз в 15 минут:
#!/bin/bash
# /home/c/user/site/private/retry.sh
F=/home/c/user/site/private/leads-retry.log
[ -s "$F" ] || exit 0
mv "$F" "$F.work"
while IFS= read -r line; do
code=$(curl -sL -o /dev/null -w '%{http_code}' -m 10
-H 'Content-Type: application/json' -d "$line" "$SHEET_WEBHOOK_URL")
[ "$code" = "200" ] || echo "$line" >> "$F"
done < "$F.work"
rm -f "$F.work"
И контрольный вопрос, который решает больше, чем весь код: как вы узнаете, что заявок нет, потому что их правда нет, а не потому что сломалась интеграция. Я обычно вешаю простую проверку: если за 72 часа в таблицу не приехало ни одной строки, скрипт шлёт в Telegram напоминание проверить форму. Ложные срабатывания на низком трафике бывают, но лучше лишний раз проверить, чем месяц собирать заявки в мусорку.
Чек-лист приёмки, по которому я сдаю такую интеграцию клиенту:
- Тестовая заявка приходит во все каналы: почта, мессенджер, таблица, лог.
- Двойной клик по кнопке даёт одну строку, а не две.
- Отключённый интернет на стороне таблицы не ломает форму (проверяется подменой URL вебхука на несуществующий).
- Заявка с кириллицей, длинным комментарием и пустым email не ломает разбор.
- Бот с honeypot отсекается и в таблицу не попадает.
- Доступ к таблице выдан поимённо, публичная ссылка отозвана.
- Файлы с ПДн лежат вне docroot и не отдаются по прямой ссылке.
Последний пункт проверяется одной командой: curl -I https://вашсайт.ру/private/leads.log. Ответ должен быть 403 или 404, а не 200 со списком телефонов.
Когда таблица начинает мешать
Признаки, что пора двигаться к нормальной системе:
- Два и более менеджера спорят, кто взял заявку в работу.
- Появились этапы сделки и нужен отчёт по конверсии между ними.
- Звонки надо слушать, а не пересказывать в колонке «заметка».
- Заявки идут не только с сайта, а ещё из мессенджеров, с Авито и с почты.
- Кто-то один раз отсортировал диапазон без заголовков и перемешал данные (классика, восстанавливать приходится из истории версий).
- Больше 60-80 заявок в месяц.
Совпало три пункта: считайте, что таблица уже стоит вам денег в виде потерянных лидов. Дальше выбор между amoCRM и Битрикс24, про их отличия и цену внедрения я писал в разборе интеграции с amoCRM и Битрикс24. Хорошая новость: накопленная таблица переезжает в CRM импортом за час, если колонки заведены так, как я описал выше. Плохая: если в ней три колонки и каша в телефонах, переезд превращается в ручную чистку на несколько дней.
Промежуточный вариант, который часто спасает: оставить таблицу как приёмник и добавить к ней канбан-доску в любом таск-трекере. Заявка падает в таблицу, менеджер руками создаёт карточку по тем, кто ответил. Черновой поток отделён от рабочих сделок, а платить за CRM пока не надо.
Что делать дальше
Если у вас на сайте сейчас работает только письмо на почту, начните с малого: добавьте в обработчик запись в лог и уведомление в мессенджер. Это полчаса работы и минус один класс проблем. Таблицу подключайте следующим шагом, когда захотите смотреть на источники.
Настройка такой связки под ключ у меня занимает 2-4 часа: обработчик, скрытые поля с UTM, приёмник в таблице, защита от дублей, крон на дозаливку и приёмка по чек-листу. Для клиентов на абонентском обслуживании это входит в часы тарифа, отдельной строкой не считается. Что входит в тарифы и сколько это стоит, смотрите в разделе с ценами.
Если заявки уже теряются и разбираться некогда, напишите через форму на главной и приложите ссылку на страницу с формой. Обычно я за вечер говорю, что именно у вас отваливается: спам-фильтр на почте, отвалившийся вебхук или форма, которая молча падает на мобильных.
Частые вопросы
Когда таблицы хватает вместо CRM
При потоке до нескольких десятков заявок в месяц и одном-двух менеджерах. Дальше начинается боль. CRM нужна, когда появляются этапы сделки, распределение заявок между людьми и история переписки по каждому клиенту.
Можно ли складывать заявки в зарубежное облако
Формально это трансграничная передача персональных данных со всеми требованиями. Российские аналоги закрывают ту же задачу и снимают вопрос целиком.
Что писать в строку таблицы
Дату и время, имя, контакт, текст заявки, страницу, с которой пришла, и источник перехода. Последние два поля потом отвечают на вопрос, какая реклама окупается.
Как не потерять заявки
Ставить таймаут на запись и всегда дублировать заявку на почту или в мессенджер. Если таблица недоступна, форма не должна ни висеть, ни терять данные.