openapi: 3.1.0 info: title: Чат-Карта — API для учётной системы (/pos) version: 1.1.0 description: | Методы сервиса «Чат-Карта», которые вызывает учётная или кассовая система организации. Ключ кассы выпускается в личном кабинете (раздел «Подключение») и передаётся в заголовке X-Api-Key. Часть методов и полей зависит от тарифа: функция не из тарифа отвечает 403 { error: plan, message } или 400 invalid с текстом «… доступно на тарифе «…» и выше». Что включено — GET /pos/plan. Подробное описание сценариев — docs/API.md. servers: - url: https://app.chat-card.ru security: - PosKey: [] tags: - name: Тариф - name: Касса - name: Покупки - name: Статусы карт - name: Удержание - name: Настройки - name: Точки регистрации - name: Рассылки paths: /pos/plan: get: operationId: getPlan tags: [Тариф] summary: Тариф организации, лимиты, включённые функции и использование description: Для дополнительного юрлица или базы действует тариф основной организации. responses: '200': description: Тариф content: application/json: schema: type: object required: [plan, usage] properties: plan: { $ref: '#/components/schemas/Plan' } usage: type: object properties: cards: { type: integer, description: Карт подключено к приложению } sources: { type: integer, description: Активных точек регистрации } campaigns: type: object properties: month: { type: string, example: '2026-10', description: Месяц по Москве } used: { type: integer, description: Рассылок запланировано и отправлено в этом месяце } limit: { type: [integer, 'null'], description: null — без ограничения } '403': { $ref: '#/components/responses/Forbidden' } /pos/card-status: post: operationId: syncCardStatus tags: [Статусы карт] summary: Сообщить статусы карт после пересчёта или ручной смены description: | Одна карта { card_code, status } или пачка до 1000 в поле cards. Учитываются только карты, подключённые к приложению. Изменение статуса отправляет вебхук card.status_changed. Повышение (по порядку status_upgrade.order или по проценту из GET /statuses) на тарифе «Сеть / PRO» с включённым status_upgrade — поздравление и баллы через POST /bonus (reason status_upgrade), один раз на карту и статус. Первый известный статус карты поздравления не даёт. requestBody: required: true content: application/json: schema: oneOf: - $ref: '#/components/schemas/CardStatus' - type: object required: [cards] properties: cards: type: array maxItems: 1000 items: { $ref: '#/components/schemas/CardStatus' } example: cards: - { card_code: '2200000000015', status: Золотая } - { card_code: '2200000000022', status: Серебряная } responses: '200': description: Принято content: application/json: schema: type: object properties: changed: { type: integer, description: У скольких карт статус изменился } upgraded: { type: integer, description: Сколько из них — повышение } '400': description: bad_card (с полем index) или too_many content: application/json: schema: { $ref: '#/components/schemas/Error' } '403': { $ref: '#/components/responses/Forbidden' } /pos/reviews: get: operationId: listReviews tags: [Удержание] summary: Оценки визитов после покупки (тариф «Сеть / PRO») description: | Только поставленные оценки. С параметром cursor — до 500 оценок в порядке времени оценки, next_cursor для следующего запроса и has_more. Покупатель оценивает визит до 7 дней после покупки, поэтому синхронизация по номеру оценки (since_id, устаревший вариант) может пропустить оценки; используйте cursor. parameters: - { name: cursor, in: query, schema: { type: string, pattern: '^(\d{13}_\d{1,15})?$' }, description: 'next_cursor из прошлого ответа; пусто — с начала' } - { name: since_id, in: query, deprecated: true, schema: { type: integer, default: 0 }, description: 'Устарело: последние 500 оценок с id больше этого' } - { name: max_rating, in: query, schema: { type: integer, minimum: 1, maximum: 5 }, description: 'Например, 3 — только плохие' } responses: '200': description: Оценки content: application/json: schema: type: object properties: reviews: type: array items: { $ref: '#/components/schemas/Review' } next_cursor: { type: string, description: Курсор для следующего запроса (только с cursor) } has_more: { type: boolean, description: 'Есть ещё оценки: запросите сразу (только с cursor)' } '400': { description: 'bad_cursor' } '403': { $ref: '#/components/responses/PlanOrForbidden' } /pos/referrals: get: operationId: listReferrals tags: [Удержание] summary: «Приведи друга» — приглашения и награды (тариф «Сеть / PRO») parameters: - { name: referrer_card, in: query, schema: { type: string }, description: Только приглашения этой карты } responses: '200': description: Приглашения content: application/json: schema: type: object properties: referrals: type: array items: { $ref: '#/components/schemas/Referral' } '403': { $ref: '#/components/responses/PlanOrForbidden' } /pos/segments: get: operationId: listSegments tags: [Удержание] summary: RFM-сегменты карт (тариф «Сеть / PRO») description: | Сегмент считается по покупкам из POST /pos/purchases для карт, подключённых к приложению. Можно записать сегмент в свойство клиента в 1С или выбрать сегмент для рассылки (audience.segment в POST /pos/campaigns). responses: '200': description: Справочник сегментов и сегмент каждой карты content: application/json: schema: type: object properties: segments: type: array items: type: object properties: id: { $ref: '#/components/schemas/SegmentId' } title: { type: string, example: VIP перестали ходить } hint: { type: string, description: Правило сегмента } cards: type: array items: type: object properties: card_code: { type: string } segment: { $ref: '#/components/schemas/SegmentId' } recency_days: { type: [integer, 'null'], description: Дней с последней покупки; null — покупок нет } frequency: { type: integer, description: Число покупок } monetary_rub: { type: number, description: Сумма покупок, ₽ } '403': { $ref: '#/components/responses/PlanOrForbidden' } /pos/redeem/consume: post: operationId: consumeRedeemToken tags: [Касса] summary: Погасить одноразовый код списания LR… description: Код живёт 60 секунд и гасится один раз. В ответ — карта, по которой в этом чеке разрешено списание баллов. requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ConsumeRequest' } example: { token: LR48201937465012, register_id: kassa-2 } responses: '200': description: Код погашен content: application/json: schema: type: object required: [card_code] properties: card_code: { type: string, example: '2200000000015' } phone: { type: string, example: '79991234567' } platform: { type: string, enum: [max, telegram] } user_id: { type: string, example: '123456789' } '403': { $ref: '#/components/responses/Forbidden' } '410': { $ref: '#/components/responses/TokenGone' } /pos/gifts/consume: post: operationId: consumeGiftToken tags: [Касса] summary: Погасить одноразовый код подарка LG… description: | Код живёт 15 минут. Касса выдаёт подарок и списывает gift.points баллов с карты в своей системе. Подарки за баллы — тариф «Бизнес» и выше, иначе 403 plan. requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ConsumeRequest' } example: { token: LG90318274650193, register_id: kassa-2 } responses: '200': description: Код погашен content: application/json: schema: type: object required: [card_code, gift] properties: card_code: { type: string } gift: type: object required: [id, name, points] properties: id: { type: integer } name: { type: string } points: { type: number } sku: { type: [string, 'null'], description: Артикул из кабинета } '403': { $ref: '#/components/responses/PlanOrForbidden' } '410': { $ref: '#/components/responses/TokenGone' } /pos/gifts: get: operationId: listGifts tags: [Касса] summary: Каталог подарков организации, включая скрытые responses: '200': description: Подарки content: application/json: schema: type: object properties: gifts: type: array items: { $ref: '#/components/schemas/Gift' } '403': { $ref: '#/components/responses/Forbidden' } /pos/purchases: post: operationId: recordPurchases tags: [Покупки] summary: Покупка или возврат по карте description: | Идемпотентно по receipt_id. Можно передать одну покупку или пачку до 500 в поле purchases. Используется для статистики, автовозврата и RFM-сегментов. Для новой (не повторной) покупки сервис также: синхронизирует card_status, планирует просьбу оценить визит (review) и награждает за первую покупку приглашённого друга (referral). Повтор того же receipt_id ничего не начисляет второй раз. requestBody: required: true content: application/json: schema: oneOf: - $ref: '#/components/schemas/Purchase' - type: object required: [purchases] properties: purchases: type: array maxItems: 500 items: { $ref: '#/components/schemas/Purchase' } example: receipt_id: 7f3c2a10-5b1e-4c2d-9a77-0e1f2a3b4c5d card_code: '2200000000015' amount: 6490 scanned_code: LC2200000000015 store_code: kazan-mega purchased_at: '2026-10-19T14:05:00+03:00' card_status: Золотая responses: '200': description: Записано content: application/json: schema: type: object properties: recorded: { type: boolean } created: { type: integer, description: Сколько чеков записано впервые } '400': description: bad_purchase (с полем index) или too_many content: application/json: schema: { $ref: '#/components/schemas/Error' } '403': { $ref: '#/components/responses/Forbidden' } /pos/loyalty-settings: get: operationId: getLoyaltySettings tags: [Настройки] summary: Настройки программы лояльности (все разделы со значениями по умолчанию) description: | Разделы функций, которых нет в тарифе, приходят с enabled: false. plan_features — какие функции включены в тариф. responses: '200': description: Настройки content: application/json: schema: allOf: - $ref: '#/components/schemas/LoyaltySettings' - type: object properties: plan_features: { $ref: '#/components/schemas/PlanFeatures' } '403': { $ref: '#/components/responses/Forbidden' } put: operationId: updateLoyaltySettings tags: [Настройки] summary: Изменить настройки description: | Меняются только переданные разделы и поля. Ответ — полные настройки после сохранения. Включить (enabled: true) функцию не из тарифа нельзя — 400 invalid; выключить и менять тексты можно. Поле plan_features в запросе игнорируется. requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/LoyaltySettings' } example: birthday: { enabled: true, days_before: 3, points: [{ status: '', amount: 300 }, { status: Золотая, amount: 1000 }] } responses: '200': description: Сохранено content: application/json: schema: { $ref: '#/components/schemas/LoyaltySettings' } '400': { $ref: '#/components/responses/Invalid' } '403': { $ref: '#/components/responses/Forbidden' } /pos/sources: get: operationId: listSources tags: [Точки регистрации] summary: Точки регистрации организации responses: '200': description: Точки content: application/json: schema: type: object properties: sources: type: array items: allOf: - $ref: '#/components/schemas/Source' - type: object properties: origin: { type: string, enum: ['1c', cabinet] } '403': { $ref: '#/components/responses/Forbidden' } put: operationId: saveSources tags: [Точки регистрации] summary: Создать или обновить точки по коду description: Если активных точек после сохранения станет больше лимита тарифа — 400 plan, ничего не сохраняется. requestBody: required: true content: application/json: schema: type: object required: [sources] properties: sources: type: array maxItems: 1000 items: { $ref: '#/components/schemas/Source' } responses: '200': description: Сохранено content: application/json: schema: type: object properties: saved: { type: integer } '400': { $ref: '#/components/responses/Invalid' } '403': { $ref: '#/components/responses/Forbidden' } /pos/campaigns: post: operationId: createCampaign tags: [Рассылки] summary: Создать и запланировать push-рассылку в MAX и Telegram description: | Ограничения тарифа (400 invalid с текстами в errors): число рассылок в месяц («Старт» — 2, «Базовый» — 4), send_at позже чем через 5 минут и фильтры statuses, sources, client_ids — с тарифа «Бизнес», segment — тариф «Сеть / PRO». Сегмент превращается в список карт в момент создания рассылки. requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CampaignRequest' } responses: '201': description: Рассылка запланирована content: application/json: schema: type: object properties: id: { type: integer } status: { type: string, enum: [scheduled] } send_at: { type: string, format: date-time } recipients: { type: integer, description: Сколько человек получат на текущий момент } '400': { $ref: '#/components/responses/Invalid' } '403': { $ref: '#/components/responses/Forbidden' } /pos/campaigns/{id}: get: operationId: getCampaign tags: [Рассылки] summary: Статус и статистика рассылки parameters: - { name: id, in: path, required: true, schema: { type: integer } } responses: '200': description: Рассылка content: application/json: schema: type: object properties: id: { type: integer } status: { type: string, enum: [scheduled, sending, sent, cancelled] } send_at: { type: [string, 'null'], format: date-time } sent_at: { type: [string, 'null'], format: date-time } recipients: { type: integer } stats: type: object properties: pending: { type: integer } sent: { type: integer } failed: { type: integer } blocked: { type: integer } '404': { $ref: '#/components/responses/NotFound' } /pos/campaigns/{id}/cancel: post: operationId: cancelCampaign tags: [Рассылки] summary: Отменить рассылку parameters: - { name: id, in: path, required: true, schema: { type: integer } } responses: '200': description: Отменена content: application/json: schema: type: object properties: id: { type: integer } status: { type: string, enum: [cancelled] } '404': { $ref: '#/components/responses/NotFound' } '409': description: already_sent — рассылка уже отправлена content: application/json: schema: { $ref: '#/components/schemas/Error' } components: securitySchemes: PosKey: type: apiKey in: header name: X-Api-Key description: Ключ кассы ck_… из кабинета («Подключение → Ключ кассы») responses: PlanOrForbidden: description: | plan — функция не входит в тариф (текст в message), или forbidden — нет ключа, ключ неверный, организация не активна content: application/json: schema: { $ref: '#/components/schemas/Error' } example: { error: plan, message: 'Оценка после покупки: доступно на тарифе «Сеть / PRO» и выше' } Forbidden: description: forbidden — нет ключа, ключ неверный или организация не активна content: application/json: schema: { $ref: '#/components/schemas/Error' } TokenGone: description: token_invalid_or_expired — код истёк, уже погашен или выдан другой организации content: application/json: schema: { $ref: '#/components/schemas/Error' } Invalid: description: invalid — ошибки проверки в errors, ничего не сохранено content: application/json: schema: { $ref: '#/components/schemas/Error' } NotFound: description: not_found content: application/json: schema: { $ref: '#/components/schemas/Error' } schemas: Error: type: object required: [error] properties: error: { type: string } errors: { type: array, items: { type: string }, description: Тексты ошибок на русском } message: { type: string, description: 'Для error = plan: какая функция и с какого тарифа доступна' } index: { type: integer, description: Номер неверной записи в пачке } ConsumeRequest: type: object required: [token] properties: token: { type: string, pattern: '^L[RG]\d{14}$' } register_id: { type: string, description: 'Номер кассы, для журнала' } store_id: { type: string, description: 'Магазин, для журнала' } Gift: type: object properties: id: { type: integer } name: { type: string } description: { type: string } points: { type: number } sku: { type: string } kind: { type: string, enum: [gift, coupon], description: 'coupon — купон партнёра (тариф «Сеть / PRO»): баллы списываются в приложении через POST /bonus/writeoff, покупатель получает промокод' } partner: { type: string, description: Партнёр купона } coupon_code: { type: string, description: Промокод купона (только в /pos/gifts) } image_asset_id: { type: [integer, 'null'] } image_url: { type: [string, 'null'], description: Путь относительно https://app.chat-card.ru } active: { type: boolean } sort: { type: integer } Purchase: type: object required: [receipt_id, card_code, amount] properties: receipt_id: { type: string, pattern: '^[\w.:/-]{1,100}$' } card_code: { type: string, description: 'Номер карты, префикс LC допустим' } amount: { type: number, description: 'Сумма в рублях, для возврата отрицательная' } scanned_code: { type: string, description: Отсканированный код; LC… и LR… — покупка картой из приложения } source: { type: string, enum: [app, card], description: Если scanned_code не передан } store_code: { type: string, maxLength: 40 } purchased_at: { type: string, format: date-time } card_status: { type: string, maxLength: 100, description: 'Статус карты после этого чека, как name в GET /statuses. Повышение — поздравление (тариф «Сеть / PRO»)' } CardStatus: type: object required: [card_code, status] properties: card_code: { type: string, description: 'Номер карты, префикс LC допустим' } status: { type: string, maxLength: 100, description: Как name в GET /statuses } SegmentId: type: string enum: [vip, vip_leaving, loyal, active, new, one_time, sleeping, lost, no_purchases] Review: type: object properties: id: { type: integer } rating: { type: [integer, 'null'], minimum: 1, maximum: 5 } comment: { type: [string, 'null'] } amount_rub: { type: [number, 'null'], description: Сумма чека } purchased_at: { type: string, format: date-time } can_rate: { type: boolean } public: description: Для оценки 5 — предложение оставить отзыв на картах oneOf: - type: 'null' - type: object properties: bonus: { type: number, description: Баллы за переход к отзыву } done: { type: boolean, description: Покупатель перешёл по ссылке } targets: type: array items: type: object properties: id: { type: string, enum: [yandex, 2gis] } title: { type: string } url: { type: string } card_code: { type: string } receipt_id: { type: string } store_code: { type: [string, 'null'] } rated_at: { type: [string, 'null'], format: date-time } public_target: { type: [string, 'null'], enum: [yandex, 2gis, null] } Referral: type: object properties: id: { type: integer } referrer_card: { type: string, description: Кто пригласил } friend_card: { type: string, description: Приглашённый друг } status: { type: string, enum: [registered, rewarded, failed], description: 'registered — друг подключил карту, rewarded — после первой покупки обоим начислены баллы, failed — начисление не удалось' } receipt_id: { type: [string, 'null'], description: Первая покупка друга } created_at: { type: string, format: date-time } rewarded_at: { type: [string, 'null'], format: date-time } PlanFeatures: type: object description: Какие функции включены в тариф additionalProperties: { type: boolean } properties: push_filters: { type: boolean } push_schedule: { type: boolean } segments: { type: boolean } history: { type: boolean } receipts: { type: boolean } gifts: { type: boolean } coupons: { type: boolean } app_code: { type: boolean } welcome: { type: boolean } birthday: { type: boolean } winback: { type: boolean } expiry: { type: boolean } review: { type: boolean } referral: { type: boolean } status_upgrade: { type: boolean } white_label: { type: boolean } webhooks: { type: boolean } roles: { type: boolean } onec_examples: { type: boolean } analytics: { type: boolean } Plan: type: object properties: id: { type: string, enum: [start, basic, business, pro] } name: { type: string, example: Бизнес } tagline: { type: string } price: type: object description: Цена за месяц, ₽, при оплате на 1, 6 и 12 месяцев properties: '1': { type: number, example: 4990 } '6': { type: number, example: 4490 } '12': { type: number, example: 3990 } limits: type: object description: null — без ограничения properties: cards: { type: [integer, 'null'] } designs: { type: [integer, 'null'] } sources: { type: [integer, 'null'] } campaigns_per_month: { type: [integer, 'null'] } users: { type: [integer, 'null'] } orgs: { type: [integer, 'null'], description: Юрлиц или баз в аккаунте } features: { $ref: '#/components/schemas/PlanFeatures' } Source: type: object required: [code, name] properties: code: { type: string, pattern: '^[A-Za-z0-9_-]{1,40}$' } city: { type: string, maxLength: 80 } name: { type: string, maxLength: 120 } active: { type: boolean, default: true } CampaignRequest: type: object required: [title, body] properties: title: { type: string, maxLength: 100 } body: { type: string, maxLength: 3000, description: С картинкой не длиннее 900 символов } button_text: { type: string, maxLength: 40 } image_url: { type: string, pattern: '^https://' } send_at: { type: string, format: date-time, description: По умолчанию — сразу } ends_at: { type: string, format: date-time, description: До какого времени акция видна в ленте mini-app } audience: type: object description: Условия складываются через «И»; пустой список — без ограничения properties: platforms: { type: array, items: { type: string, enum: [max, telegram] } } statuses: { type: array, maxItems: 50, items: { type: string, maxLength: 100 } } sources: { type: array, items: { type: string }, description: Коды точек } client_ids: { type: array, maxItems: 5000, items: { type: string }, description: 'Номера карт, телефоны, ID в мессенджере' } segment: { $ref: '#/components/schemas/SegmentId', description: 'RFM-сегмент (тариф «Сеть / PRO»); заменяет client_ids картами сегмента' } StatusPoints: type: array maxItems: 50 description: Строка с пустым status действует для всех остальных статусов items: type: object properties: status: { type: string, maxLength: 100 } amount: { type: number, minimum: 0 } LoyaltySettings: type: object properties: accrual: type: object description: Применяет учётная система при проведении чека properties: enabled: { type: boolean, default: false } levels: type: array maxItems: 50 items: type: object properties: status: { type: string, description: Пусто — все статусы } from_sum: { type: number, minimum: 0 } percent: { type: number, minimum: 0, maximum: 100 } delay_days: { type: integer, minimum: 0, maximum: 365, default: 0 } lifetime_days: { type: integer, minimum: 0, maximum: 3650, default: 0 } welcome: type: object description: Применяет учётная система в POST /register properties: enabled: { type: boolean, default: false } amount: { type: number, minimum: 0, default: 0 } lifetime_days: { type: integer, minimum: 0, maximum: 3650, default: 0 } existing_clients: { type: boolean, default: false } birthday: type: object description: Применяет сервис (POST /bonus в учётную систему и сообщение клиенту) properties: enabled: { type: boolean, default: false } days_before: { type: integer, minimum: 0, maximum: 30, default: 0 } send_hour: { type: integer, minimum: 0, maximum: 23, default: 10, description: Час по Москве } lifetime_days: { type: integer, minimum: 0, maximum: 3650, default: 14 } points: { $ref: '#/components/schemas/StatusPoints' } text: { type: string, maxLength: 1000, description: 'Подстановки {имя}, {баллы}, {дата}' } winback: type: object description: Применяет сервис по покупкам из /pos/purchases properties: enabled: { type: boolean, default: false } after_days: { type: integer, minimum: 7, maximum: 1000, default: 60 } repeat_days: { type: integer, minimum: 7, maximum: 1000, default: 90 } send_hour: { type: integer, minimum: 0, maximum: 23, default: 12 } lifetime_days: { type: integer, minimum: 0, maximum: 3650, default: 14 } points: { $ref: '#/components/schemas/StatusPoints' } text: { type: string, maxLength: 1000 } expiry: type: object description: Применяет сервис по GET /expiring учётной системы properties: enabled: { type: boolean, default: false } days_before: { type: integer, minimum: 1, maximum: 60, default: 7 } send_hour: { type: integer, minimum: 0, maximum: 23, default: 12 } min_amount: { type: number, minimum: 0, default: 1 } text: { type: string, maxLength: 1000 } gifts: type: object properties: enabled: { type: boolean, default: false } history: type: object properties: enabled: { type: boolean, default: false } birth_date: type: object properties: ask: { type: string, enum: ['off', optional, required], default: 'off' } new_card: type: object description: Вид карты для клиента, которого нет в учётной системе properties: kind: { type: string, maxLength: 100, default: '', description: "Наименование из GET /statuses; пусто — по настройкам учётной системы" } review: type: object description: Оценка визита после покупки (тариф «Сеть / PRO»). Применяет СЕРВИС по покупкам из /pos/purchases properties: enabled: { type: boolean, default: false } delay_min: { type: integer, minimum: 1, maximum: 1440, default: 15, description: Через сколько минут после чека спросить } repeat_days: { type: integer, minimum: 0, maximum: 365, default: 7, description: Не чаще раза в N дней на карту } min_amount: { type: number, minimum: 0, default: 0, description: От суммы чека, ₽ } text: { type: string, maxLength: 1000, description: 'Подстановка {имя}' } public_bonus: { type: number, minimum: 0, default: 100, description: Баллы за переход к отзыву на картах после оценки 5 } public_lifetime_days: { type: integer, minimum: 0, maximum: 3650, default: 30 } yandex_url: { type: string, description: 'https://… — страница отзывов в Яндекс Картах' } gis_url: { type: string, description: 'https://… — страница отзывов в 2ГИС' } notify_email: { type: string, description: Куда слать оценки 1–3; пусто — email организации } referral: type: object description: «Приведи друга» (тариф «Сеть / PRO»). Награда — после первой покупки друга на кассе properties: enabled: { type: boolean, default: false } friend_points: { type: number, minimum: 0, default: 300 } referrer_points: { type: number, minimum: 0, default: 300 } lifetime_days: { type: integer, minimum: 0, maximum: 3650, default: 60 } min_amount: { type: number, minimum: 0, default: 0, description: Минимальная сумма первой покупки, ₽ } friend_text: { type: string, maxLength: 1000, description: 'Подстановки {имя}, {баллы}' } referrer_text: { type: string, maxLength: 1000 } status_upgrade: type: object description: Поздравление с новым статусом карты (тариф «Сеть / PRO»). Статусы — из card_status в /pos/purchases и /pos/card-status properties: enabled: { type: boolean, default: false } points: { $ref: '#/components/schemas/StatusPoints' } lifetime_days: { type: integer, minimum: 0, maximum: 3650, default: 30 } order: { type: array, maxItems: 50, items: { type: string }, description: 'Статусы от младшего к старшему; пусто — по cashback_percent из GET /statuses' } text: { type: string, maxLength: 1000, description: 'Подстановки {имя}, {баллы}, {статус}' } WebhookEnvelope: type: object required: [event, org_id, created_at, data] properties: event: { type: string, enum: [card.registered, card.status_changed, review.created, review.negative, referral.registered, referral.rewarded] } org_id: { type: integer } created_at: { type: string, format: date-time } data: { type: object, description: Зависит от события, см. docs/API.md } webhooks: chatCardEvent: post: summary: Событие организации (тариф «Бизнес» и выше) description: | Сервис отправляет POST на адрес из кабинета («Подключение → Вебхуки»), только https. Заголовки: X-ChatCard-Event — имя события, X-ChatCard-Signature: sha256=. Ответьте 2xx в течение 5 секунд. Повторов нет: недоставленные события видны в журнале кабинета. parameters: - { name: X-ChatCard-Event, in: header, required: true, schema: { type: string } } - { name: X-ChatCard-Signature, in: header, required: true, schema: { type: string, pattern: '^sha256=[0-9a-f]{64}$' } } requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/WebhookEnvelope' } example: event: review.negative org_id: 12 created_at: '2026-10-20T09:15:00.000Z' data: { review_id: 381, card_code: '2200000000015', rating: 2, comment: Долго ждали на кассе, receipt_id: 7f3c2a10, store_code: kazan-mega, phone: '79991234567', owner_name: Ольга Иванова } responses: '200': { description: Принято }