Интеграции
Содержание раздела
Раздел «Интеграции» — точка подключения внешних систем к платформе. Интеграция — это касса, сайт или мобильное приложение, которые проводят заказы, начисляют бонусы, читают данные клиентов и узнают акции для гостя через API. Каждая интеграция получает свои API-ключи — «пропуска» с настраиваемыми правами, привязкой к филиалам и сроком действия. Заказы в разделе «Заказы» появляются именно отсюда: их создают системы, подключённые через интеграции.
Зачем это бизнесу#
- Контроль доступа по принципу минимума. Кассе — только проведение заказов и работа с бонусами, витрине сайта — только чтение. Каждая внешняя система получает ровно те права, что ей нужны, и ни правом больше.
- Мгновенное отключение. Сменился подрядчик, утёк ключ, закрылся канал — отключение интеграции одной кнопкой немедленно отзывает доступ по всем её ключам.
- Ключ на филиал. Кассовый ключ можно привязать к конкретной точке продаж — касса одного филиала не проведёт заказ от имени другого.
- Прозрачность. По каждому ключу видно, когда он создан, когда истекает и когда использовался в последний раз — «мёртвые» ключи находятся и отзываются за минуту.
Список интеграций#

Заголовок страницы — «Интеграции», подзаголовок — «Управление интеграциями и API-ключами». В правом верхнем углу — кнопка «Создать интеграцию», над таблицей — поиск и фильтры по статусу, типу и каналу (см. ограничения ниже).
В таблице — колонки Название (ссылка на карточку), Тип, Каналы, Статус, Ключей и Создана. На скриншоте — интеграция демо-организации «Demo POS»: серверная, активная, с каналами pos, web, mobile и двумя ключами.
Два типа интеграций#
| Тип | Ключи | Для чего |
|---|---|---|
| Серверная | sk_live_... (secret key) |
Бэкенды: кассовое ПО, сервер сайта, 1С. Ключ хранится на сервере и никогда не попадает к пользователям |
| Клиентская | pk_live_... (publishable key) |
Код, работающий у клиента: сайт в браузере, мобильное приложение. Дополнительно защищается списком «Разрешённых доменов» |
Правило простое: если ключ будет лежать на вашем сервере — интеграция серверная; если ключ окажется в коде сайта или приложения, куда может заглянуть любой пользователь, — клиентская, с ограничением по доменам.
У каждой интеграции задаются каналы продаж — они ограничивают, какие заказы система может проводить: интеграция с каналом «POS» не сможет прислать заказ с пометкой «Website». Канал заказа всегда должен входить в каналы интеграции.
Что касса передаёт и получает при расчёте. Для каждой позиции чека достаточно артикула, названия, цены и количества. Пределы длины: артикул — до 100 символов, название, категория и бренд — до 255; позиция с более длинным значением отклоняется ошибкой проверки (400 VALIDATION_ERROR с путём к полю, например items[0].name) и в расчёте, и в подтверждении. Точка продаж и касса описаны ниже, в «Точка продаж и касса в заказе». Категорию передавать не обязательно: платформа сама находит товар в «Каталоге» по артикулу и берёт оттуда категорию (вместе с родительскими), бренд и теги, поэтому акции по категории работают и на кассе, которая категорию не присылает. Но передавать категорию рекомендуется: подтверждённые заказы синхронизируют каталог с кассой — новые артикулы заводятся товарами с названием и категорией из чека, у существующих товаров обновляются название и переданная категория, недостающие категории создаются сами. Без категории новые товары появятся в каталоге без неё, и раскладывать их придётся вручную. Ручные правки названий в каталоге следующий чек перезапишет — правьте их в учётной системе кассы. Цена всегда берётся из чека. Если сработала акция «Подарок к покупке», в ответе придёт артикул, название и количество подарочного товара, идентификатор (promotionId) и название акции; цены у подарка нет — касса подставляет её сама по артикулу. Подарок — зона платформы: она решает, положен ли он, и при подтверждении сама сохраняет его в заказе. Касса подарок не передаёт ни в расчёт, ни в подтверждение: в items только позиции, которые клиент покупает; строка подарка в чеке нужна лишь для показа клиенту и печати. Добавляйте её с нулевой ценой и не включайте в подытог и в процент скидки: если поставить подарку каталожную цену и скидку 100%, скидка в чеке разойдётся с платформой ровно на цену подарка (платформа подарок скидкой не считает, он учитывается как «подарок × N»).
Идентификатор строки чека. Для каждой позиции касса может передать необязательное поле:
| Поле | Что передавать | Зачем |
|---|---|---|
positionId |
Свой идентификатор строки чека — номер или код строки в кассовой программе, например line-2. До 64 символов; внутри одного чека значения не должны повторяться |
Платформа возвращает его в той же строке ответа расчёта и подтверждения, а также при чтении заказа. По нему касса точно знает, к какой своей строке относится скидка, когда в чеке несколько строк с одним артикулом — например, две пиццы и к каждой модификатор «Пепперони»: строки не сливаются, у каждой своя скидка. В админке идентификатор виден в карточке заказа под артикулом позиции |
Пробелы по краям обрезаются, пустое значение равносильно непереданному. Повтор идентификатора в одном чеке или значение длиннее 64 символов отклоняются ошибкой проверки 400 VALIDATION_ERROR; для повтора в ответе указаны обе строки, например items[2].positionId — duplicates items[1].positionId. В разных чеках идентификаторы могут совпадать. В ответах расчёта и первого подтверждения строки идут в порядке запроса, но при чтении сохранённого заказа и при повторе подтверждения порядок строк не гарантирован — сопоставляйте строки по positionId, а не по номеру строки или артикулу. У подарков (giftItems) этого поля нет.
"items": [
{ "sku": "PIZZA", "name": "Пицца", "price": 500, "quantity": 1, "positionId": "line-1" },
{ "sku": "PEPPERONI", "name": "Пепперони", "price": 100, "quantity": 1, "positionId": "line-2" },
{ "sku": "PIZZA", "name": "Пицца", "price": 500, "quantity": 1, "positionId": "line-3" },
{ "sku": "PEPPERONI", "name": "Пепперони", "price": 150, "quantity": 2, "positionId": "line-4" }
]
Точка продаж и касса в заказе. Касса передаёт три необязательных поля:
| Поле | Где | Что передавать |
|---|---|---|
storeId |
Расчёт и подтверждение | Свой код точки — ID заведения в кассовой системе, до 100 символов: NAMANGAN-01 или, в rPos, строка вида 65f8de27-da49-70e5-01a0-19ba4455186f. Платформа ищет точку из раздела «Филиалы», у которой «Внешний ID» совпадает с этим кодом (пробелы по краям отбрасываются). Внутренний идентификатор точки платформы в storeId не подходит. Неактивная точка принимается |
storeName |
Расчёт и подтверждение | Название точки, до 255 символов, например Наманган, ул. Навои 12. Нужно только для точки, которой ещё нет в справочнике: платформа создаёт её с этим названием и кодом storeId, и заказ проходит. У существующей точки название не меняется — переименовывают в админке. Без storeName неизвестный storeId даёт 404 LOCATION_NOT_FOUND (Location with externalId '…' not found). Создавать точку может только ключ с правом на запись заказов (orders:write, а также orders:* или *): в расчёте ключ только с orders:read при неизвестном storeId и заполненном storeName получает 403 API_KEY_SCOPE_REQUIRED — так же, как создание клиента в расчёте требует customers:write. Для подтверждения orders:write нужен и так |
salePointName |
Только подтверждение | Номер или имя кассы (терминала), как оно настроено в кассовой программе, до 100 символов, например POS-2. Сохраняется в заказе как есть — справочника касс в платформе нет. Пробелы по краям обрезаются, пустое значение не сохраняется. Возвращается в ответе подтверждения и при чтении заказа в поле salePointName |
Рекомендуем всегда передавать storeName вместе с storeId и выдавать кассовому ключу право orders:write: тогда открытие новой точки не потребует заранее заводить её в админке. Автоматически созданная точка помечена в разделе «Филиалы» тегом «Создан кассой» — администратор дополняет её адрес. В ответах и вебхуках точка заказа приходит уже внутренним идентификатором платформы.
{
"channel": "pos",
"storeId": "NAMANGAN-01",
"storeName": "Наманган, ул. Навои 12",
"salePointName": "POS-2",
"orderNumber": "674",
"items": [{ "sku": "LAT-001", "name": "Латте", "price": 550.00, "quantity": 1 }]
}
Точка влияет и на акции: акция, ограниченная филиалами (см. «Промо-акции» → «Где действует»), применяется, только если storeId указывает на точку из её списка.
В списке применённых акций ответа (appliedPromotions) только акции, которые что-то дали: скидку больше нуля, подарок, бесплатную единицу или бонусы. Акция, подошедшая по условиям, но не давшая выгоды, в список не попадает.
Что приходит в ответе подтверждения и при чтении заказа. Подтверждённый заказ хранит сработавшие акции и подарки. Ответ подтверждения, чтение заказа по идентификатору или внешнему номеру и ответ возврата содержат appliedPromotions — по объекту на акцию: идентификатор, название, скидка на позициях, начисленные бонусы, число бесплатных единиц и подарки этой акции, — и общий список подарков giftItems в том же формате, что и в расчёте. Обратите внимание: в ответе расчёта appliedPromotions — это список идентификаторов акций, а в ответе подтверждения и чтения заказа — список объектов. Если касса разбирает оба ответа одним кодом, учтите разницу.
Подтверждение заказа: передавайте суммы из расчёта. При подтверждении платформа всегда пересчитывает заказ заново. Между расчётом и подтверждением суммы могут измениться — например, акция закончилась или её приостановили. Чтобы гость не оплатил сумму, отличную от показанной на кассе, передавайте в подтверждении итоговую сумму и сумму скидки из ответа расчёта (expectedFinalTotal, expectedDiscountTotal). Если они разошлись с пересчётом, заказ не подтверждается: касса получает ошибку 409 ORDER_CALCULATION_DRIFT с актуальными суммами, показывает гостю новую сумму и подтверждает заказ повторно. Без этих полей заказ подтверждается молча с новыми суммами.
Подтверждение заказа: номер с чека и внешний ID заказа. В подтверждении касса может передать два разных номера заказа, оба необязательные:
| Поле | Что передавать | Зачем |
|---|---|---|
externalOrderId |
Внутренний идентификатор заказа в кассовой системе — то, по чему касса сама отличает заказы (rPos, например, присылает UUID заказа). Буквы, цифры, ., _, :, -, до 100 символов |
Уникален в пределах организации: по нему касса читает заказ и оформляет возврат, повтор с тем же значением и новым ключом идемпотентности отклоняется (409 ORDER_EXTERNAL_ID_CONFLICT). В админке — строка «Внешний ID заказа» в карточке |
orderNumber |
Номер, напечатанный на чеке, — например 674 или web-49803. До 100 символов, без переводов строки и других управляющих символов |
По нему менеджер находит заказ в разделе «Заказы», когда гость приходит с чеком. Номер не уникален: на кассе номера повторяются между сменами и точками, платформа такие повторы принимает. В админке — колонка и строка «Номер заказа» |
Интеграторам rPos: в externalOrderId по-прежнему передаётся внутренний id заказа rPos, номер с чека — в orderNumber. Пробелы по краям номера обрезаются, пустой номер не сохраняется. Номер записывается только при подтверждении (в расчёте его нет); при повторе подтверждения с тем же ключом идемпотентности остаётся номер, сохранённый первым запросом. В ответах подтверждения, чтения и возврата заказа номер приходит в поле orderNumber.
Статус интеграции — Активна или Отключена. Отключение действует мгновенно и отзывает доступ по всем ключам сразу — это аварийный рубильник на случай инцидента. Кроме того, промо-акции можно ограничивать конкретными интеграциями — акция «только для мобильного приложения» настраивается в разделе «Промо-акции».
Карточка интеграции и API-ключи#

Карточка показывает таблицу «API-ключи» и блок «Сведения» (количество ключей, каналы, разрешённые домены, даты создания и изменения, идентификатор); тип и статус интеграции — в шапке. На скриншоте — карточка «Demo POS» с двумя показательными ключами:
- Глобальный ключ «Demo POS Key» — права «*» (полный доступ), филиалы «Все филиалы», без срока действия;
- Филиальный ключ «Касса — Кофейня в Magic City» — гранулярные права
orders:write,customers:read,bonuses:write, привязка к филиалу «Кофейня в Magic City» и срок действия до 1 июля 2027 года.
Колонки таблицы ключей:
| Колонка | Что показывает |
|---|---|
| Название | Название ключа и рядом статус: «Активен», «Отозван» или «Истёк». Под ними — маска ключа (значок рядом есть только у активных ключей и копирует полный ключ: админка запрашивает его у сервера, а если браузер не дал записать в буфер обмена, открывает окно с ключом для ручного копирования) и строка «Использован: ‹дата и время›» или «Ещё не использовался» |
| Доступ | Права ключа тегами, под ними — филиалы: «Все филиалы» у глобального ключа или названия точек продаж через запятую |
| Действует | Дата создания ключа, под ней — «до ‹дата›» или «бессрочно» |
Последняя колонка — действия, она закреплена у правого края таблицы. У активных ключей в ней кнопки «Показать ключ» (иконка глаза) и «Отозвать», у отозванных и истёкших она пустая. Если у интеграции ещё нет ключей, вместо таблицы — надпись «Ключей пока нет».
При создании ключа настраиваются:
| Настройка | Варианты |
|---|---|
| Права | Полный доступ («*») или гранулярно: клиенты / заказы / бонусы × чтение / запись, «Карты лояльности: чтение / запись» (loyalty-cards:read, loyalty-cards:write; см. «Карты лояльности»), а также «Акции: чтение» (promotions:read) |
| Филиалы | Список точек продаж, от имени которых работает ключ. Пусто = все филиалы |
| Срок действия | Дата, после которой ключ перестаёт работать. Удобно для временных подрядчиков |
Сразу после создания ключ показывается целиком — с предупреждением, что его нужно сохранить. В таблице ключи всегда маскируются: видны только первые символы. Кнопка «Показать ключ» (иконка глаза) позволяет администратору посмотреть ключ повторно — каждый такой просмотр фиксируется платформой.
Кнопка «Отозвать» прекращает действие ключа необратимо — восстановить отозванный ключ нельзя, только выпустить новый. Это осознанно: при малейшем подозрении на утечку ключ отзывается без колебаний.
Право «Акции: чтение»#

На скриншоте — окно «Создать API-ключ» интеграции «Demo POS»: в списке прав выбраны «Клиенты: чтение» и «Акции: чтение». В форме ключа у права «Акции: чтение» подсказка «Список актуальных акций для клиента по телефону». Оно нужно кассе, сайту или приложению, которые показывают гостю его акции ещё до оформления покупки. Гость называет телефон, и система возвращает акции, которые действуют прямо сейчас. Каждая акция в списке отмечена одним из двух способов:
- Персональная — действует именно на этого гостя: он состоит в сегменте, на который нацелена акция; или подходит под условия акции по своему профилю (уровень, возраст, день рождения, число заказов); или цепочка выдала ему личный промокод. Такие акции касса видит первыми, вместе с личными промокодами гостя.
- Публичная — работает для всех. Если для неё нужен промокод (например, общий
WELCOME10), это тоже видно в списке.
Акции, которые гостю заведомо не подходят, в список не попадают: например, акция для сегмента «VIP» не покажется гостю вне этого сегмента. Также учитываются ограничения акции по интеграциям: акция «только для мобильного приложения» кассе не покажется.
Передавайте код своей точки (storeId). Касса может добавить в запрос акций тот же storeId, что и в расчёте заказа. Тогда в списке только акции, которые действуют в этой точке: акции с условием «Точка продаж» на другую точку и акции, ограниченные другими филиалами, не придут, а промокод такой акции получит ответ «акция гостю сейчас недоступна». Без storeId условие «Точка продаж» список не сужает, а акции, ограниченные полем «Филиалы», не показываются вовсе — поэтому касса, работающая в конкретной точке, должна передавать storeId всегда. Точку этот запрос не создаёт: неизвестный код даёт 404 LOCATION_NOT_FOUND, storeName здесь не передаётся. Пример анонимного запроса: GET /v1/api/promotions/available?storeId=NAMANGAN-01; с телефоном: POST с телом {"phone": "+998 90 100-00-01", "storeId": "NAMANGAN-01"}.
Незнакомый номер — не ошибка, касса получит только публичные акции. Список можно запросить и без телефона, например для витрины сайта, пока гость не вошёл: тогда тоже придут только публичные акции. Условия на сумму чека, товары и время на этом шаге не проверяются, поэтому окончательную скидку показывает только расчёт заказа.
В том же запросе можно проверить промокод, который назвал гость, ещё до корзины. Система ответит, действует ли код и какую акцию он даёт, а если нет — почему: кода нет, он выключен, ещё не начал действовать или истёк, исчерпан лимит, код только для определённых сегментов, или его акция гостю сейчас недоступна. Проверка ничего не списывает и на список акций не влияет.
Как проверить на демо-данных. В организации, заполненной демо-данными (см. «Настройки» → «Демо-данные»), есть интеграция «Demo POS» с ключом полного доступа, право «Акции: чтение» в него входит. Подключите кассу или приложение этим ключом и запросите акции для нескольких гостей:
- +998 90 100-00-03, Тимур Рахимов — VIP-гость: первой придёт персональная акция «VIP: +5% бонусов».
- +998 90 100-00-01, Алишер Каримов — у него есть личный промокод: персональной придёт «−15% по промокоду из письма» вместе с кодом вида
GIFT-XXXXXX. - +998 90 100-00-19, Азиз Хакимов — у него один заказ: персональной придёт «Второй визит: 10 000 бонусов».
- +998 90 999-99-99 — такого гостя в базе нет: придут только публичные акции, ошибки не будет.
Кроме персональных, все четверо получат одни и те же публичные акции, например «Базовый кешбэк 5%» и «Каждый третий кофе — бесплатно». Сам промокод Алишера при каждом заполнении новый, постоянно только то, что он есть. Если в разделе «Промо-акции» нет акции «Второй визит: 10 000 бонусов», демо-данные заполнены более ранней версией. Чтобы результат совпал с описанным, удалите данные демонстрационной организации и заполните её заново.
Кто что может#
| Роль | Доступ к разделу |
|---|---|
| Администратор организации (TENANT_ADMIN) | Полное управление: интеграции, ключи, отзыв, отключение |
| Все остальные роли | Раздел не виден |
API-ключи — самый чувствительный элемент безопасности организации, поэтому доступ максимально узкий: раздел видит только Администратор организации.