Разработчику
Подпись посетителя
Ваш сервер подписывает токен, и чат знает, какой вошедший посетитель пишет.
На странице
Если посетители входят в аккаунт на вашем сайте, ваш сервер может сказать чату, кто пишет. Тогда:
- операторы видят аккаунт и поля, которые вы передали;
- посетитель находит свой разговор на любом устройстве и на любом домене проекта.
Без подписи браузер хранит разговор отдельно для каждого сайта. На новом устройстве или зеркале посетитель начинает заново.
Как это работает
- Ваш сервер подписывает токен для вошедшего пользователя секретом подписи проекта.
- Страница передаёт токен чату: через
data-identityна теге илиidentify(token). - Виджет отправляет токен в
POST https://api.tolkly.io/v1/projects/<project>/identify. Сервер проверяет его и говорит, какой разговор принадлежит посетителю: тот, что в этом браузере, а если его нет — последний открытый разговор посетителя.
Сам виджет ничего не проверяет. Если сервер отклонил токен, ему не доверяют даже частично: чат работает как анонимный.
Токен
Токен — это JWT с подписью HS256. Сервер всегда читает алгоритм как HS256, что бы ни стояло в
заголовке. Если в заголовке другой алгоритм, включая none, токен отклоняется.
| Поле | Обязательно | Что это |
|---|---|---|
sub | да | ID пользователя у вас, строка (число принимается как строка), до 200 символов. С более длинным токен отклоняется. |
name | нет | Имя, которое видят операторы, до 120 символов. |
email | нет | Отбрасывается, если не похоже на почту. |
phone | нет | Отбрасывается, если не похоже на номер телефона. |
fields | нет | Объект с полями, которые объявил проект (ниже). |
exp | нет | Срок действия в секундах от начала эпохи. Без него токен действует, пока действует секрет. |
exp проверяется с запасом в 60 секунд. Так немного отстающие часы не отклонят свежий токен.
Токен длиннее 8192 символов отклоняется без чтения.
Содержимое токена подписано, но не зашифровано: посетитель может прочитать его на странице. Передавайте только то, что ему можно видеть.
Поля
Поля у каждого проекта свои: номер счёта, статус KYC, VIP-уровень, ссылка на пользователя в вашей
админке. Каждое поле проект объявляет с ключом и типом. Необъявленные поля в fields
отбрасываются, как и значения неверного типа. Поля объявляют владельцы и администраторы компании.
| Тип | Принимает |
|---|---|
text | строку до 500 символов (число принимается как текст) |
number | число или строку с числом |
date | YYYY-MM-DD или полное время в ISO 8601 |
list | массив строк или чисел, каждое до 100 символов; сохраняются первые 20. Одно значение считается одним элементом. Если хоть один элемент пустой, слишком длинный или другого типа, отбрасывается весь список. |
link | URL http: или https:, до 2000 символов |
boolean | true или false (а также "true", "false", 1, 0) |
Поля объявляются в разделе проекта «Установка» › «Дополнительно», блок «Поля посетителя»: название, которое видят операторы, ключ, который пишет ваш сервер, тип и показывать ли значение операторам. Ключ после создания не меняется: ваш сервер уже его присылает, поэтому новый ключ — новое поле. Выключите «Показывать операторам» для значения, которое нужно только правилам, например оценки риска.
Секрет
Владельцы и администраторы компании находят его в разделе проекта «Установка»: «Дополнительно» › блок «Подпись посетителя» › «Показать секрет».
Храните секрет на сервере сайта, в переменной окружения, например TOLKLY_IDENTITY_SECRET.
Никогда не кладите его в код для браузера или в страницу: с ним можно войти под любым вашим
клиентом.
Подпись на вашем сервере
Node.js:
// npm install jsonwebtoken
import jwt from 'jsonwebtoken'
const token = jwt.sign(
{
sub: String(user.id),
name: user.name,
email: user.email,
},
process.env.TOLKLY_IDENTITY_SECRET,
{ algorithm: 'HS256', expiresIn: '12h' },
)PHP:
// composer require firebase/php-jwt
use Firebase\JWT\JWT;
$token = JWT::encode([
'sub' => (string) $user->id,
'name' => $user->name,
'email' => $user->email,
'exp' => time() + 12 * 3600,
], getenv('TOLKLY_IDENTITY_SECRET'), 'HS256');Python:
# pip install pyjwt
import os, time, jwt
token = jwt.encode({
"sub": str(user.id),
"name": user.name,
"email": user.email,
"exp": int(time.time()) + 12 * 3600,
}, os.environ["TOLKLY_IDENTITY_SECRET"], algorithm="HS256")Добавьте fields рядом с остальными полями, например fields: { vip: user.vip }.
Передайте токен чату
Если страница собирается для вошедшего пользователя, поставьте токен на тег:
<tolkly-chat endpoint="https://api.tolkly.io" project="your-project"
data-identity="<token>"></tolkly-chat>Если пользователь вошёл без перезагрузки страницы, передайте токен из скрипта:
await customElements.whenDefined('tolkly-chat')
document.querySelector('tolkly-chat').identify(token)Элемент также следит за data-identity: если поменять атрибут, чат заново определит посетителя.
При выходе вызовите reset() и перестаньте выводить data-identity. Чат забудет посетителя и
разговор, сохранённый в этом браузере, и следующий человек за этим компьютером начнёт заново. Если
в браузере лежит разговор другого подписанного посетителя, сервер тоже выдаст новый разговор.
Поручить ИИ-агенту
У владельцев и администраторов есть кнопка «Поручить ИИ-агенту»: «Установка» › «Дополнительно», блок «Подпись посетителя». Она даёт промпт ниже, с уже подставленным проектом, для агента, который правит сайт (Claude Code, Cursor). По этому промпту агент:
- подписывает токен на сервере секретом из
TOLKLY_IDENTITY_SECRET; - передаёт токен через
data-identityилиidentify(token); - вызывает
reset()при выходе; - проверяет результат в карточке посетителя в инбоксе.
Промпт называет переменную окружения, но не сам секрет. Здесь вместо идентификатора проекта стоит
your-project. Скопировать промпт: «Открыть в…» › «Промпт для ИИ-агента».
Без подписи
identify({ id, name, email, phone, fields }) называет посетителя без токена. Операторы видят эти
данные с пометкой «Не проверено». Они сохраняются только в этом разговоре. Они не попадают в
историю между устройствами и не забирают разговор подписанного посетителя.
Отклонённый токен
Чат работает как анонимный и пишет причину в консоль браузера:
| Код | Почему |
|---|---|
malformed | Не три части base64url с JSON, нет sub или он длиннее 200 символов, exp не число, или токен длиннее 8192 символов. |
algorithm | В заголовке другой алгоритм, не HS256. |
signature | Подписан другим секретом: старым или от другого проекта. |
expired | Срок exp истёк, даже с учётом допустимого расхождения часов. |
Смена секрета
Нажмите «Выпустить новый секрет» в приложении (только для владельцев и администраторов). Старый секрет сразу
перестаёт работать. Подписанные им токены отклоняются с кодом signature, пока ваш сервер не
начнёт подписывать новым. Поэтому меняйте секрет вместе с выкладкой.