Appearance
API попапов для SDK
Раздел описывает серверный API, по которому SDK получает попапы и отправляет события по ним. Он нужен разработчикам SDK (JS, iOS, Android, React Native, Flutter) и тем, кто пишет собственный клиент для показа попапов. Для обычной интеграции на сайте и в приложении он не требуется: SDK работает с ним сам.
API находится на домене личного кабинета, а не на api.rees46.ru:
https://app.rees46.ru/api/v3/popupsДля white label кластеров домен свой (app.personaclick.com, app.rees46.kz и т.д.), а магазин со своим доменом личного кабинета передает его SDK в настройках (опция app_host в JS SDK).
Общие правила
- Посетитель идентифицируется параметрами
shop_id,didиsid— теми же, что и в остальных методах SDK. Cookie, авторизация иwithCredentialsне используются: API отвечает сAccess-Control-Allow-Origin: *. - Ответ всегда JSON в стандартном формате:
{"status": "success", "payload": {...}}или{"status": "fail", "message": "..."}. - Условия показа проверяются в два этапа: сервер отбирает попапы по аудитории, SDK показывает их по триггерам и условиям страницы (экрана). Подробнее — в разделах для сайта и in-app.
Проверка проекта
Одинакова для всех методов и выполняется первой:
| Код | Сообщение | Когда |
|---|---|---|
| 400 | Invalid request parameters | нет shop_id, did или sid; неизвестный channel или event |
| 404 | Resource not found | проект с таким shop_id не найден; попап не найден или не на витрине |
| 403 | Access denied | проект отключен |
| 402 | Payment required | проект не оплачен или у него нет продукта «Попапы» |
| 429 | Too many requests | превышен лимит событий (см. ниже) |
Как SDK узнает, что попапы есть
Ответ /init содержит флаг popups_v3: true, если у проекта есть продукт «Попапы» и хотя бы один опубликованный попап нужного канала (для stream ios/android — канала mobile, иначе web; в тестовом режиме считаются и попапы в статусе «Тест»). Без флага SDK в API попапов не обращается — это экономит запрос на каждой странице у проектов без попапов.
json
{
"did": "...",
"popups_v3": true
}Поле popup в ответах /init и /push относится к попапам предыдущей версии и для новых попапов не используется.
Список попапов
Серверный этап: попапы проекта, доступные посетителю. Список нужно кешировать на ttl секунд (5 минут) и запрашивать заново при старте сессии и после событий, которые меняют аудиторию: покупки, записи профиля, подписки, добавления в сегмент.
GET https://app.rees46.ru/api/v3/popupsПараметры
| Параметр | Тип | Обязателен? | Описание |
|---|---|---|---|
| shop_id | String | Да | API-ключ |
| did | String | Да | Device ID |
| sid | String | Да | Идентификатор сессии |
| channel | String | Нет | web (по умолчанию) или mobile |
| device | String | Нет | Только для web: desktop или mobile. Без параметра определяется по User-Agent |
| testmode | Boolean | Нет | true — тестовый режим: в ответ попадают и попапы в статусе «Тест» |
| debug | Boolean | Нет | 1 — добавить в ответ список отсеянных попапов с причинами |
Запрос
shell
curl "https://app.rees46.ru/api/v3/popups?shop_id=...&did=...&sid=...&channel=mobile"Ответ
json
{
"status": "success",
"payload": {
"ttl": 300,
"popups": [
{
"id": 512,
"layout": "modal",
"placement": "center",
"selector": null,
"trigger_type": "time_on_screen",
"trigger_value": 10,
"trigger_event": null,
"frequency": "once_day",
"cooldown_minutes": 60,
"promo_code_list_id": 7,
"target_screens": ["product", "cart"],
"design": { "frameStyle": {}, "rows": [], "elements": [], "variables": {} }
}
],
"excluded": [
{ "id": 513, "reason": "segments" }
]
}
}excluded есть только при debug=1. Причины: period — вне периода показа, device — не то устройство, segments — не подходит по сегментам, subscription — не подходит по подписке на рассылки.
Что проверил сервер: статус попапа и наличие опубликованной версии, период показа в часовом поясе проекта, сегменты, статус подписки на канал рассылок, устройство (только web). Посетитель без профиля считается не подписанным и не входящим ни в один сегмент.
Поля попапа
Общие для обоих каналов:
| Поле | Тип | Описание |
|---|---|---|
| id | Integer | Идентификатор попапа |
| layout | String | Макет. web: popup, bar, widget, embed; mobile: banner, modal, fullscreen |
| placement | String | Расположение: center, top, bottom, bottom_left, bottom_right, selector |
| selector | String / null | CSS-селектор контейнера для embed |
| trigger_type | String | Триггер показа, см. ниже |
| trigger_value | Integer / null | Значение триггера: мс для delay, проценты для scroll_percent, страницы для page_views, секунды для time_on_screen |
| trigger_event | String / null | Имя события для track_event |
| frequency | String | once_session, once_life, once_day, once_week, once_month, every_page |
| cooldown_minutes | Integer | Пауза между показами в минутах, 0 — без паузы |
| promo_code_list_id | Integer / null | Список персональных промокодов для |
Только channel=web:
| Поле | Тип | Описание |
|---|---|---|
| web_devices | String | both, desktop, mobile |
| page_types | Array[String] | home, product, category, cart, purchase, search, custom; пусто — любые |
| url_include | Array[String] | Правила «URL содержит», * — любые символы; пусто — без ограничений |
| url_exclude | Array[String] | Правила «URL не содержит» |
| product_ids | Array[String] | Только на страницах этих товаров |
| category_ids | Array[String] | Только на страницах этих категорий |
| html | String | Готовый фрагмент: разметка, <style> и <script> попапа |
Только channel=mobile:
| Поле | Тип | Описание |
|---|---|---|
| target_screens | Array[String] | Коды экранов, на которых показывать; пусто — любой экран |
| design | Object | Дерево дизайна для нативной отрисовки, см. Формат дизайна |
Триггеры
| trigger_type | Канал | Когда срабатывает |
|---|---|---|
immediate | web | сразу после загрузки страницы |
delay | web | через trigger_value мс |
scroll_percent | web | при прокрутке до trigger_value % высоты страницы |
exit_intent | web | курсор ушел за верхнюю границу окна; на мобильных не срабатывает |
page_views | web | с trigger_value-й страницы за сессию |
cart_add | web | событие корзины; полная синхронизация — только если товаров стало больше |
cart_remove | web | событие удаления из корзины |
wishlist_add | web | событие добавления в избранное |
wishlist_remove | web | событие удаления из избранного |
app_start | mobile | при старте сессии |
screen_open | mobile | при открытии экрана из target_screens |
time_on_screen | mobile | через trigger_value секунд после открытия экрана |
track_event | mobile | при отправке события с именем trigger_event |
manual | mobile | только по вызову из кода приложения |
Что делает SDK после получения списка
- Ждет триггер попапа; у
webперед показом проверяетweb_devices,page_types,url_include/url_exclude,product_ids,category_ids; уmobile—target_screens. - Проверяет частоту и паузу. Они хранятся на устройстве: ключ — идентификатор попапа, время последнего показа и сессия последнего показа. В тестовом режиме показы не записываются.
- Следит за конкуренцией: одно модальное окно (
popup,modal,fullscreen) одновременно;barиwidget— не больше трех;embedбез ограничений. Если слот занят, попап показывается после закрытия предыдущего. - Если в попапе есть
и заданpromo_code_list_id, запрашивает промокод методомget_promo_codeдо показа. Нет кода — попап не показывается. - Отправляет событие
show, затем остальные события по действиям посетителя.
Попап по идентификатору
Для ручного показа из кода (r46('popup_v3', id), триггер manual). Условия аудитории не проверяются, но попап должен быть опубликован или, при testmode=true, в статусе «Тест».
GET https://app.rees46.ru/api/v3/popups/{id}Параметры
| Параметр | Тип | Обязателен? | Описание |
|---|---|---|---|
| shop_id | String | Да | API-ключ |
| did | String | Да | Device ID |
| sid | String | Да | Идентификатор сессии |
| testmode | Boolean | Нет | true — разрешить попап в тесте |
Ответ
Поля попапа — как в списке, набор зависит от канала попапа.
json
{
"status": "success",
"payload": {
"popup": { "id": 512, "layout": "modal", "...": "..." }
}
}Попап не найден, черновик, на паузе или без опубликованной версии — 404.
События попапа
По событиям строится отчет попапа в личном кабинете: показы, уникальные показы, подписки, конверсия.
POST https://app.rees46.ru/api/v3/popups/{id}/eventsПараметры
Тело application/json. Для отправки при уходе со страницы (navigator.sendBeacon) параметры можно передать в строке запроса, а тело — как text/plain.
| Параметр | Тип | Обязателен? | Описание |
|---|---|---|---|
| shop_id | String | Да | API-ключ |
| did | String | Да | Device ID |
| sid | String | Да | Идентификатор сессии |
| event | String | Да | show, close, click, submit, subscribe, prize |
| channel | String | Нет | Только для subscribe: канал подписки email, sms, web_push, mobile_push, telegram, whatsapp, max |
| event | Когда отправлять |
|---|---|
show | попап показан |
close | посетитель закрыл попап |
click | клик по ссылке или кнопке попапа, кроме закрытия |
submit | отправлена форма профиля или оценка опроса |
subscribe | подписка через форму сбора email |
prize | колесо фортуны выдало приз |
Канал
channel здесь — канал подписки, а не канал попапа. Значение web или mobile отклоняется с 400.
Запрос
shell
curl -X POST "https://app.rees46.ru/api/v3/popups/512/events" \
-H "Content-Type: application/json" \
-d '{"shop_id": "...", "did": "...", "sid": "...", "event": "subscribe", "channel": "email"}'Ответ
json
{
"status": "success"
}Правила обработки:
- событие принимается только для попапа на витрине (опубликован или в тесте, есть опубликованная версия), иначе
404; - повтор того же события того же попапа в той же сессии в течение 5 секунд не записывается, ответ
200; - не больше 120 событий в минуту с одного устройства, сверх лимита —
429; - устройство без профиля (не было ни одного
/initстарого API или записи профиля) —200без записи: событие некому приписать.
Действия внутри попапа — запись профиля, подписка, добавление в сегмент, выдача промокода, оценка NPS/CSAT/CES — SDK выполняет обычными методами CDP. API событий только фиксирует факт для отчета.
Формат дизайна
Для канала mobile попап приходит деревом дизайна, которое приложение рисует нативными элементами. Для web то же дерево на сервере уже собрано в html.
json
{
"frameStyle": {
"width": 92, "widthMode": "%", "maxWidth": 480, "maxWidthMode": "px",
"height": 320, "heightMode": "auto",
"bgColor": "#ffffff", "borderColor": "#e5e7eb", "borderWidth": 1, "borderRadius": 16, "padding": 20
},
"rows": [
{
"id": "row-1",
"columns": [50, 50],
"cells": [{ "align": "left", "verticalAlign": "top" }, { "align": "center", "verticalAlign": "center" }],
"gap": 8, "minHeight": 110,
"bgColor": null, "bgImageUrl": null, "bgSize": "cover", "bgPosition": "center", "bgRepeat": "no-repeat"
}
],
"elements": [
{ "id": "el-1", "type": "text", "rowId": "row-1", "partIndex": 0, "style": { "message": "Скидка 10%", "fontSize": 18 } },
{ "id": "el-2", "type": "button", "rowId": "row-1", "partIndex": 1, "style": { "buttonText": "Купить", "buttonAction": "link", "linkHref": "{{ios_link}}" } }
],
"variables": { "ios_link": "myapp://catalog/sale" }
}frameStyle— рамка попапа: ширина (widthMode%илиpx,maxWidth), высота (heightModeautoилиpx), фон, рамка, скругление, отступ.rows— строки сверху вниз.columns— ширины колонок в процентах,cells— выравнивание содержимого колонки (align:left/center/right,verticalAlign:top/center/bottom). Фон строки — цвет или картинка.elements— плоский список элементов;rowIdиpartIndexуказывают строку и номер колонки. Внутри колонки элементы идут в порядке списка. Элемент соstyle.cellVerticalAlignпереопределяет вертикальное выравнивание ячейки для себя.variables— статические переменные дизайна. Токенв текстах и ссылках заменяется на значение изvariables. Так задаются, например, отдельные ссылки для iOS и Android: в кнопке, а значение — вvariables.- Переменные витрины подставляются при показе, в
variablesих нет:promo_code(персональный промокод изpromo_code_list_id),cart_total,remaining,progress,campaign_end,wheel_prize.
Общие стили элемента
Любой элемент может содержать в style: bgColor, textColor, borderWidth, borderColor, borderRadius, paddingX, paddingY, fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, opacity, тень shadowY/shadowBlur/shadowColor, выравнивание в ячейке cellAlign/cellVerticalAlign, а также ссылку на весь элемент linkHref и linkTarget (_blank/_self), если элемент не кнопка и не форма.
Тексты (message, buttonText, подписи) могут содержать облегченную разметку: **жирный**, *курсив*, __подчеркнутый__, ~~зачеркнутый~~.
Элементы канала mobile
Приложение должно уметь рисовать эти типы; элементы других типов в попапе канала mobile создать нельзя.
| type | Что рисовать | Основные поля style |
|---|---|---|
text | текст | message, messageAlign |
box | контейнер с необязательным текстом | как text, плюс фон и отступы |
image | картинка | imageUrl, imageAlt, imageHeightMode (auto/px/%), imageHeight, imageObjectFit, imageRadius, imageAspectRatio |
button | кнопка | buttonText, buttonAction (link/close/copy), linkHref, copyText, buttonAlign, buttonBgColor, buttonTextColor, buttonFontSize, buttonPaddingX, buttonPaddingY, buttonRadius |
close | кнопка закрытия | как button; closeIconMode (text/image/svg), closeIconSize, imageUrl, closeSvg |
emailCollector | поле email, согласие, кнопка | emailPlaceholder, emailSubmitText, emailConsentLabel, emailConsentFields (email_bulk, email_chain, email_transactional), emailSegmentId, emailErrorMessage, emailSuccessTitle, emailSuccessMessage, emailLayout (inline/column), цвета полей и кнопки |
form | форма профиля | formFields — список {id, kind, label, placeholder, required, propertyKey}, kind: email, phone, first_name, last_name, birthday, gender, custom; formConsentLabel, formConsentFields, formSegmentId, formSubmitText, formErrorMessage, formSuccessTitle, formSuccessMessage |
promoCode | промокод с кнопкой копирования | promoSource (personal — , static — promoStaticCode), promoLabel, promoShowCopy, promoCopyText, promoCopiedText, promoApplyHref, цвета и размер кода |
survey | опрос NPS / CSAT / CES | surveyType (nps 0–10, csat 1–5, ces 1–7), surveyCampaignId, surveyCampaignCode, surveyQuestion, surveyMinLabel, surveyMaxLabel, surveyCommentPlaceholder, surveySubmitText, surveyThanksText, цвета шкалы |
recommendation | блок товарных рекомендаций | recommenderCode — код блока, товары запрашиваются методом рекомендаций |
Что должно происходить по действиям — так же, как в JS SDK:
| Действие | Что делает SDK |
|---|---|
кнопка link | открыть linkHref; событие click |
кнопка close / элемент close | закрыть попап; событие close |
кнопка copy | скопировать copyText (или текст кнопки); событие click |
emailCollector | записать профиль {email} с флагами из emailConsentFields = true; при emailSegmentId — добавить в сегмент; события subscribe с channel: email |
form | записать профиль из полей (custom — в свойство propertyKey); согласие — флаги formConsentFields; при formSegmentId — добавить в сегмент; событие submit |
promoCode personal | до показа получить код из promo_code_list_id (get_promo_code); копирование — событие click |
survey | отправить оценку и комментарий в кампанию опроса с каналом popup (методы nps review, csat review, ces review); событие submit |
recommendation | запросить блок по recommenderCode и отрисовать товары |