#JS API виджета
Статус: Планируется — объект
Sapportи его методы не существуют в текущей версииwidget.js. Доступно — только два сообщенияpostMessageиз кадра чата и ключwindow.sapport.consentзагрузчика (раздел «Что есть сегодня»). Не используйте планируемые методы в боевой разметке: вызовSapport.open()сегодня завершится ошибкойSapport is not defined.
Назначение: управлять виджетом из кода вашей страницы — открыть и закрыть окно, отправить сообщение от имени посетителя, сообщить виджету о смене страницы и о личности посетителя, подписаться на события.
#Содержание
- Когда использовать
- Что есть сегодня (Доступно)
- Подключение (Планируется)
- Методы (Планируется)
- События (Планируется)
- identify и подпись личности (Планируется)
- Одностраничные приложения (Планируется)
- Ошибки
- Заметки
- Открытые вопросы
#Когда использовать
| Задача | Метод |
|---|---|
| Открыть чат по своей кнопке «Задать вопрос» | Sapport.open() вместе с data-launcher="none" |
| Сообщить, что посетитель перешёл на другую «страницу» без перезагрузки | Sapport.setPage() |
| Связать посетителя с пользователем вашего сайта | Sapport.identify() с подписью |
| Реагировать на сообщение ИИ, заявку или передачу оператору | Sapport.on() |
| Отправить заранее заготовленный вопрос от имени посетителя | Sapport.send() |
Доступ: аутентификация не нужна для вызовов в браузере. Подпись для identify создаёт ваш сервер секретом интеграции; секрет в браузер не попадает.
#Что есть сегодня (Доступно)
Статус: Доступно.
#Сообщения postMessage из кадра чата
Окно чата — это iframe на домене ячейки. Он сообщает странице-хозяину о двух событиях сообщениями window.postMessage. Их принимает сам widget.js; ваша страница тоже может слушать их.
| Сообщение (строка) | Когда | Что делает widget.js |
|---|---|---|
sapport:new-message | Пришло новое сообщение ИИ или оператора | Если окно закрыто, увеличивает счётчик на кнопке |
sapport:close | Посетитель нажал ✕ в шапке окна | Закрывает окно |
Свойства:
- Сообщение — строка, не объект; без полезной нагрузки (текст сообщения не передаётся).
sapport:new-messageкадр отправляет на точный origin страницы-хозяина. Origin определяется поlocation.ancestorOrigins[0]или, если его нет, поdocument.referrer. Если ваш сайт отключил реферер (Referrer-Policy: no-referrer), а браузер не даётancestorOrigins(Firefox), сообщение не придёт.sapport:closeкнопка закрытия отправляет получателю*. Не доверяйте ему как источнику данных, проверяйтеevent.sourceиevent.origin.
Слушать на своей странице:
<script>
window.addEventListener('message', function (e) {
// origin ячейки, откуда загружен widget.js
if (e.origin !== 'https://formula-cream.pro') return;
if (typeof e.data !== 'string') return;
if (e.data === 'sapport:new-message') {
console.log('В чате новое сообщение');
}
if (e.data === 'sapport:close') {
console.log('Посетитель закрыл окно чата');
}
});
</script>
Эти сообщения — внутренний протокол между кадром и
widget.js, а не обещанный контракт: состав сообщений может измениться. Для устойчивой интеграции дождитесьSapport.on().
#window.sapport.consent (загрузчик)
Если виджет подключён через загрузчик, состояние согласия посетителя передаётся функцией:
window.sapport.consent(true); // согласие дано
window.sapport.consent(false); // согласие не дано / отозвано
или событием:
window.dispatchEvent(new CustomEvent('sapport:consent', { detail: true }));
// допустима и форма { granted: true }
Подробности и готовая разметка баннера — Встраивание виджета, блок 5.
#Что НЕ существует сегодня
- Объект
window.Sapportи все его методы. - Событий
ready,open,close,message,lead,handoff. - Способа открыть окно из своего кода. Описание обходных путей — блок 7.
- Способа сообщить виджету о смене SPA-маршрута:
window.sapportPageиdocument.titleчитаются один раз при первом открытии окна.
#Подключение (Планируется)
Статус: Планируется. Целевой вид; имена и сигнатуры могут уточниться при публикации.
Объект window.Sapport создаётся скриптом widget.js. Вызывайте методы после его загрузки — например, из обработчика onload тега:
<script
src="https://formula-cream.pro/widget.js"
data-channel="00000000-0000-4000-8000-000000000000"
data-launcher="none"
async
onload="Sapport.on('ready', function () { Sapport.open(); })"></script>
Очередь вызовов до загрузки скрипта проект не описывает; не рассчитывайте на неё. Гарантия порядка — событие ready.
#Методы (Планируется)
Статус: Планируется. Все методы ниже — проект. Возвращаемые значения, кроме указанных, не фиксируются.
| Метод | Параметры | Описание |
|---|---|---|
Sapport.open() | — | Открыть окно. Создаёт кадр при первом вызове |
Sapport.close() | — | Закрыть окно |
Sapport.toggle() | — | Переключить |
Sapport.send(text) | text — строка, не более 4000 знаков | Отправить сообщение от имени посетителя, как будто он набрал его сам. Требует согласия, если арендатор его включил |
Sapport.setPage(page) | {type, id, title} | Обновить контекст страницы: тип и идентификатор сущности и заголовок |
Sapport.identify(user) | {external_id, name, email, phone, signature} | Связать посетителя с пользователем вашего сайта (ниже) |
Sapport.on(event, fn) | event — имя события, fn — обработчик | Подписаться на событие |
#Sapport.setPage
| Поле | Тип | Обязательный | Ограничения | Пример |
|---|---|---|---|---|
type | строка | нет | как у window.sapportPage.type: латинские буквы, _, -, до 20 знаков, без цифр (см. ограничение 6) | course |
id | строка | нет | буквы, цифры, _, -, до 40 знаков | hydrolat-basics |
title | строка | нет | до 150 знаков; в промпт попадает очищенным | Основы гидролатов |
Sapport.setPage({ type: 'course', id: 'hydrolat-basics', title: 'Основы гидролатов' });
Адрес страницы берётся из location; без согласия (см. Страница, источник и UTM) в промпт попадает только путь.
#Sapport.send
Sapport.send('Сколько стоит курс?');
Сообщение уходит тем же маршрутом, что и ввод посетителя, — через клиентский Chat API. Ответ ИИ появится в окне; для получения ответа в коде подпишитесь на событие message.
#События (Планируется)
Статус: Планируется. Содержимое
detail(поля событий) проект не фиксирует; состав ниже — предложение документа и подлежит проверке при реализации.
| Событие | Когда | Предполагаемые данные |
|---|---|---|
ready | Виджет загружен, настройки канала получены | — |
open | Окно открылось | — |
close | Окно закрылось | — |
message | В окне появилось сообщение: посетителя, ИИ или оператора | sender (user, ai, operator), content |
lead | По диалогу создан лид | идентификатор лида не передаётся посетителю — вероятно, только факт |
handoff | Диалог передан оператору (оператор подключился или диалог встал в очередь) | — |
Sapport.on('message', function (m) {
if (m.sender === 'ai') console.log('Ответил ИИ:', m.content);
});
Sapport.on('lead', function () {
// Например, отправить цель в вашу аналитику
if (window.ym) window.ym(12345678, 'reachGoal', 'chat_lead');
});
Sapport.on('handoff', function () {
console.log('Подключается оператор');
});
Событие
leadпоказывает, что заявка создана на платформе. Для передачи заявки на ваш сервер используйте вебхуки (lead.created, а для передачи оператору —conversation.handoff_requested), а не обработчик в браузере: браузер можно закрыть или подделать.
#identify и подпись личности (Планируется)
Статус: Планируется.
identify сообщает платформе, какой пользователь вашего сайта сейчас в чате. Без подписи любой посетитель мог бы выдать себя за другого, подставив чужой external_id в консоли браузера. Поэтому external_id принимается только с подписью, созданной вашим сервером.
| Поле | Тип | Обязательный | Ограничения | Пример |
|---|---|---|---|---|
external_id | строка | да | Стабильный идентификатор пользователя в вашей системе | user-4815 |
signature | строка (hex) | да | HMAC-SHA256(secret интеграции, external_id), шестнадцатеричная строка | 9f2c… |
name | строка | нет | Анна | |
email | строка | нет | [email protected] | |
phone | строка | нет | +79990001122 |
Секрет интеграции (в проекте — «secret интеграции») хранится только на вашем сервере. Никогда не помещайте его в код страницы и не отдавайте браузеру.
#Схема
Браузер Ваш сервер Страница
│ GET /page │
│ ─────────────────────────► │ signature = HMAC(secret, external_id)
│ ◄───────────────────────── │ в HTML кладётся external_id + signature
│ │
│ Sapport.identify({ external_id, signature, … }) ─────────► виджет
#Подпись на Node.js
const crypto = require('node:crypto');
// SECRET — секрет интеграции, из переменной окружения вашего сервера
function signExternalId(externalId) {
return crypto
.createHmac('sha256', process.env.SAPPORT_IDENTIFY_SECRET)
.update(externalId)
.digest('hex');
}
// в обработчике страницы:
// const signature = signExternalId(String(user.id));
#Подпись на PHP
<?php
// $secret — секрет интеграции из конфигурации сервера
function sapport_sign_external_id(string $externalId): string {
$secret = getenv('SAPPORT_IDENTIFY_SECRET');
return hash_hmac('sha256', $externalId, $secret);
}
$signature = sapport_sign_external_id((string)$user['id']);
#Подпись на Python
import hashlib
import hmac
import os
def sign_external_id(external_id: str) -> str:
secret = os.environ["SAPPORT_IDENTIFY_SECRET"].encode()
return hmac.new(secret, external_id.encode(), hashlib.sha256).hexdigest()
signature = sign_external_id(str(user.id))
#Вызов в браузере
<script>
// значения подставляет ваш сервер при формировании страницы
Sapport.identify({
external_id: 'user-4815',
signature: '0000000000000000000000000000000000000000000000000000000000000000',
name: 'Анна',
email: '[email protected]'
});
</script>
Значение подписи в примере — заглушка, а не рабочая подпись.
Алгоритм хеша (
SHA-256) и кодировка (hex) выбраны документом по аналогии с подписью вебхуков; проект фиксирует лишь «HMAC отexternal_idсекретом интеграции». Уточняется при реализации.
#Одностраничные приложения (Планируется)
Виджет читает адрес и заголовок страницы при первом открытии. В SPA адрес меняется без перезагрузки, и ИИ получил бы устаревший контекст. Вызывайте Sapport.setPage() при смене маршрута.
#React (React Router)
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
export function SapportPageSync({ type, id, title }) {
const location = useLocation();
useEffect(() => {
if (!window.Sapport) return;
window.Sapport.setPage({ type, id, title: title || document.title });
}, [location.pathname, type, id, title]);
return null;
}
// Страница курса
<SapportPageSync type="course" id={course.slug} title={course.title} />
#Vue (Vue Router)
// router.js
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({ history: createWebHistory(), routes });
router.afterEach((to) => {
if (!window.Sapport) return;
window.Sapport.setPage({
type: to.meta.pageType,
id: to.params.slug,
title: document.title
});
});
export default router;
#Next.js (App Router)
'use client';
import { usePathname } from 'next/navigation';
import { useEffect } from 'react';
export function SapportRouteSync() {
const pathname = usePathname();
useEffect(() => {
(window as any).Sapport?.setPage({ title: document.title });
}, [pathname]);
return null;
}
Тег виджета в Next.js подключайте через next/script с strategy="afterInteractive":
import Script from 'next/script';
<Script
src="https://formula-cream.pro/widget.js"
data-channel="00000000-0000-4000-8000-000000000000"
strategy="afterInteractive"
/>
Сегодня (до выхода setPage) обходной путь для SPA: не полагаться на автоматический контекст и задавать window.sapportPage до первого открытия; после открытия окна контекст обновить нельзя (блок 4).
#Ошибки
JS API работает в браузере и не возвращает HTTP-ошибок. Условия, при которых вызов не имеет эффекта, проект не описывает. Предполагается:
| Ситуация | Предполагаемое поведение |
|---|---|
| Вызов до загрузки скрипта | Sapport is not defined: очередь проектом не описана |
send без согласия, когда арендатор его требует | Сообщение не уходит; окно показывает плашку согласия |
identify с неверной подписью | Личность не принимается; посетитель остаётся анонимным |
Это не контракт; точное поведение будет описано при выпуске.
#Заметки
- Вызовы
open()до готовности виджета не должны создавать второй кадр: ожидайтеready. - Каждая вкладка браузера начинает отдельный диалог: сессия хранится в
sessionStorageкадра (Клиентский Chat API).identifyв проекте предназначен, в том числе, для связывания таких диалогов с пользователем. - Статус публикации JS API и параметров v2 смотрите в журнале изменений.
#Открытые вопросы
- Порядок выхода JS API и параметров виджета v2 не зафиксирован. К владельцу.
- Точные поля
detailсобытийmessage,lead,handoff, очередь вызовов до загрузки и возвращаемые значения методов в проекте не заданы. Техническое. - Алгоритм подписи
identify: проект называет толькоHMAC(secret интеграции, external_id); хеш-функция и кодировка подписи не зафиксированы. Не указано также, где арендатор получает «secret интеграции» и как его ротировать. Техническое. - Должен ли
identifyобъединять диалоги посетителя из разных вкладок и устройств, и что происходит с уже накопленной анонимной историей, — не описано. К владельцу.