Контакты и согласия
Список, который вы можете защитить, а не просто список, который у вас есть
Десять тысяч адресов электронной почты может держать кто угодно. Вопрос, который действительно важен — тот, который регулятор, провайдер и ваша собственная совесть задают чуть разными словами, — это откуда взялся каждый из них и на каком основании вы можете ему писать. ClickSomo превращает это в сохранённый факт, а не в то, что вам пришлось бы восстанавливать.
Идентификатор — это адрес электронной почты
Один контакт на адрес на проект — это гарантирует база данных, а не надежда на аккуратность. Адреса нормализуются при записи, а не при чтении: если нормализовать при чтении, ограничение превращается в украшение, которое пропускает и Anna@, и anna@.
Номера телефонов хранятся в формате E.164, если они есть, но телефон — это атрибут, а не идентичность. Здесь нет графа разрешения идентичностей, протокола слияния и журнала аудита слияний, потому что каждый контакт приходит по каналу, у которого уже есть адрес: импорт, форма, подписка или ваш собственный код.
Имя хранится целиком
display_name сохраняется ровно так, как было введено, и никогда не разбивается на части. Разбиение имён — фабрика ошибок: «van der Berg», «Ким Мин Джун», однословные имена, титулы и любой порядок, кроме англоязычного. А неверное разбиение всплывает в merge-теге как «Уважаемый ван,» — это хуже, чем никакой персонализации.
Структурные части существуют только для источников, которые предоставляют их уже разделёнными. Ничего не выводит их само. И имя, которое человек ввёл сам, никогда не перезаписывается более поздним CSV, случайно зафиксировавшим что-то иное: стратегия «последняя запись побеждает» позволяет победить самому небрежному импорту.
- 1ИдентичностьЭлектронная почта, нормализованная. Уникальна в рамках проекта.
- 2ИмяХранится целиком.
first_nameвычисляется при отрисовке и никогда не хранится. - 3АтрибутыЧто угодно, в формате JSON. Ваши данные, подставляемые как
attrs.*. - 4Теги и спискиТеги — полноценные сущности и живут в одном хранилище; списки — явное членство.
- 5СогласиеОдна строка на канал, с правовым основанием и его подтверждением.
- 6СобытияЖурнал с дополнением в конце (append-only) обо всём, что делал этот человек. Таблица, на которой держится весь продукт.
- ×НикогдаНи пароля, ни хеша, ни сессии, ни API-ключа. Колонка, аутентифицирующая кого-либо, — это инцидент, а не функция.
Ограничено и арендатором, и проектом — обоими сразу
Реестр согласий
Отсутствие — это не разрешение
Одна строка на контакт на канал — электронная почта, SMS, письмо, push — каждая содержит состояние, правовое основание, источник, подтверждение, отметки времени и юрисдикцию. Контакту, у которого нет строки основания для канала, нельзя отправить ничего по этому каналу. Не «помечен как запрещённый». Нельзя.
consent
Человек активно попросил. Отправка формы с сохранённым подтверждением, двойное подтверждение (double opt-in), галочка, которую он поставил сам. Это самое сильное основание и единственное, которое может породить форма.
legitimate_interest
Лучшее, чем может оказаться импорт из CSV. Таблица не может помнить, что человек на что-то согласился, поэтому ей никогда не разрешается утверждать это — и повторная загрузка того же файла не повышает основание.
contract
Вы пишете им потому, что вы оба — стороны чего-то. Напоминание об обслуживании, уведомление о продлении — то, чего они были бы раздосадованы не получить.
Перед отправкой кампании вы видите состав оснований её аудитории: сколько из этих людей попросили сами, скольких вы достигаете на основании законного интереса и сколько здесь из-за договора. Это одно число, которое остановило больше плохих рассылок, чем любой когда-либо написанный предупреждающий баннер.
Почему это строже, чем вам требуется: дело на самом деле не в вас. Репутация массовых рассылок — общий ресурс, и одна небрежная база отравляет колодец для всех, кто отправляет с той же инфраструктуры. Уровни жалоб и отказов отслеживаются по каждому арендатору, и аккаунт, превысивший порог, ограничивается здесь — прежде чем это сделает провайдер для всех сразу.

За каждой строкой в списке стоит чьё-то «да».
Четыре пути
Каждый путь в ваш список — это вы, действующие со своими данными
Здесь нет канала приёма через сервисные токены и никакого соседнего продукта, тихо пишущего в ваши контакты. Если вы хотите видеть кого-то в списке, туда его попал один из этих четырёх путей.
Импорт CSV
Сопоставление колонка за колонкой, нормализация при записи, проверка MX, чтобы мёртвые и ошибочные домены отсеивались до жёсткого отказа в доставке, и основание, которое вы задаёте явно. Импорт получает идентификатор, и каждый созданный им контакт ссылается на него.
Форма ClickSomo
Отрисовывается из того же документа, что и всё остальное, с валидацией, honeypot-полем и ограничением частоты. Отправка формы — единственный путь, который может законно породить consent, потому что это единственный случай, когда человек присутствует лично.
Страница, созданная вами в dash-b
Конструктор дашбордов публикует страницы; форма на такой странице отправляет данные сюда с вашими учётными данными. Та же валидация, та же обработка согласия, никакой второй интеграции для поддержки.
Публичный API
POST /v1/contacts с вашим собственным токеном, семантикой upsert и поддержкой ключа идемпотентности. Ваше приложение уже знает, кто зарегистрировался; это кратчайший путь от этого факта к воронке.
Сегменты — это предикат, компилируемый
Сегмент — это сохранённое дерево предикатов по полям контактов, атрибутам, тегам, принадлежности к спискам и поведению, компилируемое в SQL и кэшируемое вместе со счётчиком. Операторы обычные — равно, не равно, в, не в, начинается с, заканчивается на, содержит, задан, не задан — и комбинируются через and, or и not на любую нужную вам глубину.
Предварительно просмотрите его перед использованием. Эндпоинт возвращает реальные контакты, поэтому на вопрос «кто здесь на самом деле» вы отвечаете, посмотрев, а не отправив и узнав.
Сегменты затем несут двойную нагрузку: как аудитория кампании, как условие на ветке воронки и как триггер сами по себе — segment_enter запускает выполнение в тот момент, когда кто-то становится подходящим.
- 1Тег
tags contains "regular" - 2Атрибут
attrs.total_orders >= 3— ваше поле, ваше название для него. - 3ПоведениеНет события
order.paidза последние 90 дней. - 4СогласиеПригодное основание для e-mail. Контакты без него не входят в подсчёт, потому что им всё равно нельзя отправить.
- iПредпросмотр412 контактов, перечислено. Пересчитывается при отправке, потому что сегмент — это вопрос, а не снимок.
Компилируется в SQLСчётчик кэшируется, ответ живой
Исключения и когда они проверяются
Исключения действуют на уровне проекта и глобально: жёсткие отказы, жалобы, отписки, ручные записи и ролевые адреса. Они проверяются в момент отправки, а затем ещё раз внутри самой службы отправки.
Второе слово и делает работу. Воронка с трёхдневным ожиданием даёт трёхдневное окно, в котором кто-то может отписаться, и система, проверившая исключения при постановке в очередь, всё равно отправит ему письмо. Проверка при отправке — это разница между уважением отписки и её записью.
Удаление работает так же: запрос на удаление убирает контакт и его историю сообщений здесь и достигает каждой родственной службы, хранившей копию, через тот же внутренний канал, которым пользуется всё семейство. Есть одно место, куда просить, и одно, что происходит.