Метки: эндпоинт проверки посетителя
Метка — это решение сайта «этого посетителя показать иначе»: цвет строки в «Кто на сайте», полоса у карточки диалога, бейдж, подъём наверх списка. Решение принимает ваш сервер, чат только отображает.
Запрос #
POST с JSON-телом на адрес, указанный в панели. Приходит, когда у посетителя изменился набор ключей, истёк ttl прошлого ответа или пришло уведомление. Посетителей без ключей чат не проверяет.
| Заголовок | Значение |
|---|---|
Content-Type | application/json |
X-DD-Timestamp | Unix-время отправки, секунды |
X-DD-Signature | sha256= + hex HMAC-SHA256 от строки <timestamp>.<сырое тело> на серверном секрете |
User-Agent | DirectDialog-Marks/2 |
{
"event": "visitor.marks",
"version": 2,
"site": "shop.ru",
"visitor": {
"id": 1842,
"first_seen_at": "2026-08-02T09:14:05.000Z",
"session_started_at": "2026-09-14T10:02:41.000Z"
},
"keys": [
{ "key": "user:98765", "verified": true }
],
"data": [
{ "key": "plan", "value": "Бизнес", "verified": false }
]
}
site— код вашего сайта в чате,visitor.id— внутренний номер посетителя (его же вводят в кнопке «Проверить»).keys— ровно то, что передала страница.verified— подтверждён ли ключ токеном.data— поля из канала 1б; для решения обычно не нужны, доверять им нельзя.- Неизвестные поля игнорируйте: протокол может расширяться без смены версии.
Проверка подписи #
Эндпоинт публичный, поэтому две проверки выполняются до любой работы с данными — свежесть и подпись над сырым телом запроса (пересериализация JSON изменила бы байты и сломала бы сравнение).
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_DD_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_DD_SIGNATURE'] ?? '';
if (!ctype_digit($ts) || abs(time() - (int)$ts) > 300) { fail(401, 'stale'); }
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, DD_SERVER_SECRET);
if (!hash_equals($expected, $sig)) { fail(401, 'bad_signature'); }
Ответ #
200, Content-Type: application/json, UTF-8, не больше 64 КБ, за 3 секунды. Массив marks обязателен, даже пустой.
{
"ttl": 3600,
"marks": [
{
"key": "vip",
"priority": 10,
"expires_at": "2027-03-26T00:00:00Z",
"tooltip": "Клиент с годовой подпиской",
"live_row_bg": "#aaddcc6b",
"live_row_bg_dark": "#2e7d6440",
"chat_rail_bottom": "#2e9e7a",
"badge": { "text": "Бизнес", "color": "#2e9e7a" },
"counter_icon": "⭐️",
"max_prefix": "⭐ Приоритетный"
}
]
}
Ничего не показываем: { "ttl": 900, "marks": [] }
Поля метки #
| Поле | Что делает | Ограничения |
|---|---|---|
key | Имя метки: vip, paid, debtor | обязательно, [a-z0-9_-], до 48 символов |
priority | Больше 0 — посетитель поднимается вверх «Кто на сайте» и списка диалогов (оператор может выключить подъём у себя). Из нескольких меток в каждом поле побеждает метка с бо́льшим приоритетом | целое −1000…1000, по умолчанию 0 |
expires_at | Метка гаснет сама в этот момент, без нового запроса | ISO-8601; в прошлом или нечитаемое — метка отброшена |
live_row_bg | Фон строки в «Кто на сайте» | только hex: #rgb, #rgba, #rrggbb, #rrggbbaa |
live_row_bg_hover | Фон при наведении. Не задан — чат затемнит основной сам | hex |
chat_rail_bottom | Полоса по нижней грани карточки диалога | hex |
…_dark | live_row_bg_dark, live_row_bg_hover_dark, chat_rail_bottom_dark — для тёмной темы панели | hex; не заданы — берётся светлый вариант |
badge | {text, color} — плашка в «Кто на сайте» и в шапке диалога | text до 40 символов, color — hex |
tooltip | Подсказка при наведении | до 200 символов |
counter_icon | Значок для счётчика в шапке «Кто на сайте»: «Сейчас на сайте 12 (⭐️ 3)» | до 16 символов |
max_prefix | Первая строка карточки нового диалога в мессенджере MAX | до 60 символов |
Меток в ответе — не больше 10. Тексты выводятся как обычный текст, лишняя длина обрезается. Цвет принимается только в hex: значение уходит в CSS панели, и любой другой синтаксис игнорируется.
Как чат трактует ответ #
| Ответ | Что делает чат |
|---|---|
200 + marks | Заменяет метки посетителя присланными (пустой массив — снимает все). Следующая плановая проверка через ttl секунд (значение зажимается в 300…86400, по умолчанию 3600) |
не-2xx, редирект, таймаут, не JSON, нет marks | Прежние метки остаются, повтор через 10 минут |
Не можете ответить достоверно — отвечайте ошибкой, а не пустым списком. База недоступна, файл не читается, сторонний сервис молчит — верните 503: прежние метки сохранятся. Пустой marks — это «у него ничего нет», и выделение снимется со всех.
Рекомендуемые ttl: посетитель с меткой — 3600, без метки — 900. При работающем уведомлении изменение доедет раньше, ttl — страховка.
Требования к эндпоинту #
- Только
https://, доступен из интернета, отвечает за 3 секунды, тело до 64 КБ. - Без редиректов: любой
3xxсчитается ошибкой. Указывайте конечный адрес — без переадресации на слэш,wwwили другой домен. - Секреты боевого и тестового контуров разные; храните их в конфигурации окружения, не в git.
- В ответе — только поля из этого документа: никаких email, данных платежей и персональных данных.