{"openapi":"3.1.0","info":{"title":"LazyCRM API","version":"1","summary":"Гости, SMS и процессинг лояльности ресторана","contact":{"name":"Поддержка LazyCRM","url":"https://lazycrm.ru/docs/api"},"description":"Сайт доставки, приложение или подрядчик ресторана синхронизирует к себе базу гостей с суммой\nзаказов и датой последнего заказа и запускает SMS-рассылку по сегменту LazyCRM, не заходя в\nкабинет. Документация с примерами — https://lazycrm.ru/docs/api.\n\n## Ключ и права\nКлюч выпускает сам ресторан: кабинет → Интеграции → API. Один ключ — один ресторан: ресторан\nопределяется только ключом, параметров `tenant_id`/`user_id` нет — они дают\n400 `unknown_parameter`. Права ключа (scope) указаны у каждой операции в `security`:\nключ «только чтение» — `guests:read`, `campaigns:read`; «полный доступ» — ещё `campaigns:write`.\n\nТестовый ключ `lcrm_test_…` работает на демо-ресторане: гости демонстрационные, отправка\nсимулируется (статусы, счётчики и события идут как у настоящей рассылки), деньги не списываются.\n\n## Формат\n- Id — непрозрачная строка с префиксом типа: `gst_` гость, `seg_` сегмент, `cmp_` рассылка,\n  `evt_` событие.\n- Деньги — строка с двумя знаками и валюта рядом: `{\"amount\": \"1500.00\", \"currency\": \"RUB\"}`.\n- Момент — RFC 3339 в UTC (`2026-10-14T09:30:00Z`); дата заказа — `YYYY-MM-DD` в поясе ресторана.\n- Неизвестное значение — `null`. Поля `status`, `kind`, `failure_reason` и типы событий\n  расширяемые (`x-extensible-enum`): клиент обязан спокойно принимать новые значения.\n- Неизвестный query-параметр или поле тела — 400 `unknown_parameter` с его именем.\n- Внутри `/v1` изменения только добавляющие: новые поля, операции, значения расширяемых списков.\n\n## Ошибки\nЛюбая ошибка — статус 4xx/5xx и `application/problem+json` (RFC 9457) с машинным `code` и\n`request_id`; заголовок `Request-Id` есть в каждом ответе. Ветвитесь по `code`, а `title` и\n`detail` показывайте человеку — их текст может уточняться. Все коды — примеры ответов каждой\nоперации и страница https://lazycrm.ru/docs/api/errors.\n\n## Повтор запроса\n`POST` принимает заголовок `Idempotency-Key` (например, UUID), у отправки рассылки он обязателен.\nПовтор с тем же ключом в течение 24 часов вернёт сохранённый ответ (заголовок\n`Idempotent-Replayed: true`) и ничего не выполнит второй раз. Тот же ключ с другим запросом —\n422 `idempotency_key_reused`; первый запрос ещё выполняется — 409 `idempotency_request_in_progress`.\n\n## Лимиты\n- 600 запросов в минуту на ключ; сверх — 429 `rate_limited` и заголовок `Retry-After`.\n- Не больше 20 отправок рассылок в сутки на ресторан (по его времени); 21-я — 429\n  `send_limit_reached` с `Retry-After` до полуночи ресторана. Получателей в одной рассылке\n  не ограничиваем.\n- Частотный лимит ресторана действует и для API: гость получает не больше одной SMS за окно,\n  сколько бы рассылок ни создал интегратор. Поэтому ошибка «отправил 20 раз один сегмент»\n  стоит одну рассылку, а не двадцать. Обойти его через API нельзя.\n- Баланс SMS отправку не блокирует — как в кабинете.\n\n## Что отдаётся и что нет\n- Телефон и имя гостя отдаются в открытом виде: это база ресторана, выпустившего ключ.\n  Email, день рождения, пол и бонусы в v1 не отдаются.\n- Аудитория рассылки — только сегменты LazyCRM; рассылки по своему списку телефонов нет.\n- Отправка уходит без подтверждения человеком — как кнопка «Отправить сейчас» в кабинете.\n  Контрольная группа и отправитель (имя в SMS) — по настройкам ресторана, полей в API нет.\n- Рассылка из API видна в кабинете в общем списке с пометкой «через API»."},"servers":[{"url":"https://app.lazycrm.ru"}],"tags":[{"name":"loyalty","description":"Серверный Wallet checkout: scoped credential отдельной программы, точные денежные значения, единая продажа, повторы и исходные квитанции возврата. Credential не предназначен для браузера или мобильного приложения."},{"name":"guests","description":"Гости ресторана: поиск по телефону, гость по id и выгрузка всей базы.\n\nСумма и даты пересчитываются из заказов CRM ресторана (iiko, Goulash, Frontpad) после синка —\nс задержкой до часа после заказа. Подменные номера агрегаторов (не телефон гостя) в API не\nпопадают.\n\nСинхронизация: пройдите все страницы `GET /v1/guests`, сохраните `updated_at` последней\nзаписи, а в следующий раз зовите с `updated_after` — придут только изменившиеся гости."},{"name":"segments","description":"Сегменты ресторана с числом гостей и охватом рассылки. Состав сегментов пересчитывается по\nрасписанию; пороги задаёт ресторан в кабинете, через API они не меняются."},{"name":"campaigns","description":"SMS-рассылка по сегменту: черновик → отправка (с защитой от повтора) → статус и доставка.\n\nОтписавшиеся, гости без телефона и попавшие под частотный лимит ресторана исключаются\nавтоматически. Счётчики `stats` только растут; доставка досчитывается ещё сутки после `sent`."},{"name":"events","description":"События рассылок: вебхук на адрес ресторана и журнал `GET /v1/events` для сверки.\n\n## Вебхук\nАдрес вебхука указывается в кабинете: Интеграции → API → Вебхук (только https). При\nподключении один раз показывается секрет подписи вида `whsec_…`. LazyCRM шлёт `POST` с\nJSON-событием; успех — любой ответ 2xx в течение 10 секунд, иначе повторы с нарастающей\nпаузой около трёх с половиной суток. Порядок и однократность не гарантируются —\nобрабатывайте событие идемпотентно по его `id`, а после простоя приёмника сверяйтесь\nс журналом.\n\nСобытие тонкое: за данными идите в `GET /v1/campaigns/{campaign_id}`. Новый смысл события —\nновый `type`, старый не меняется.\n\n## Подпись\nПодпись — по спецификации Standard Webhooks: заголовки `webhook-id`, `webhook-timestamp`,\n`webhook-signature`. Подписывается строка `{webhook-id}.{webhook-timestamp}.{тело}` ключом\nиз секрета (base64 после `whsec_`) по HMAC-SHA256. Отклоняйте запросы старше 5 минут.\n\n```python\nimport base64, hashlib, hmac, time\n\ndef verify(secret: str, headers: dict, body: bytes) -> bool:\n    msg_id, ts = headers[\"webhook-id\"], headers[\"webhook-timestamp\"]\n    if abs(time.time() - int(ts)) > 300:\n        return False\n    key = base64.b64decode(secret.removeprefix(\"whsec_\"))\n    signed = f\"{msg_id}.{ts}.\".encode() + body\n    expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()\n    return any(hmac.compare_digest(\"v1,\" + expected, s) for s in headers[\"webhook-signature\"].split())\n```"}],"paths":{"/v1/guests":{"get":{"operationId":"guests_list","summary":"Выгрузить базу гостей","description":"Вся база постранично в порядке `updated_at, id`. Для синка сохраните `updated_at` последней\nзаписи и зовите с `updated_after`. Платная опция «Выгрузка базы по API» — подключается у\nменеджера LazyCRM; без неё 403 `paid_feature_required`, а найти одного гостя можно через\n`GET /v1/guests/lookup`.","parameters":[{"name":"updated_after","in":"query","required":false,"description":"Только гости, изменённые позже этого момента (RFC 3339 с поясом)","schema":{"type":"string","format":"date-time","examples":["2026-10-13T00:00:00Z"]}},{"name":"segment_id","in":"query","required":false,"description":"Только гости этого сегмента","schema":{"type":"string","examples":["seg_loyal"]}},{"$ref":"#/components/parameters/CursorParams.limit"},{"$ref":"#/components/parameters/CursorParams.cursor"}],"responses":{"200":{"description":"Страница гостей","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestList"}}}},"400":{"description":"Кривой запрос","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"},"invalid_cursor":{"$ref":"#/components/examples/invalid_cursor"}}}}},"401":{"description":"Нет ключа или он не подходит","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"}}}}},"403":{"description":"Нет права или платной опции","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"},"paid_feature_required":{"$ref":"#/components/examples/paid_feature_required"}}}}},"422":{"description":"Значение не прошло проверку","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["guests"],"security":[{"ApiKey":["guests:read"]}]}},"/v1/guests/lookup":{"get":{"operationId":"guests_lookup","summary":"Найти гостя по телефону","description":"Один гость по номеру телефона, например на оформлении заказа. Входит в базовый доступ.\nНет гостя — 404 `not_found`, кривой номер — 422 `validation_error` с причиной `invalid_phone`.\nПример: `GET /v1/guests/lookup?phone=%2B79001234567`.","parameters":[{"name":"phone","in":"query","required":true,"description":"Телефон гостя, лучше в E.164","schema":{"type":"string","examples":["+79001234567"]}}],"responses":{"200":{"description":"Гость","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Guest"}}}},"400":{"description":"Неизвестный параметр","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"}}}}},"401":{"description":"Нет ключа или он не подходит","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"}}}}},"403":{"description":"У ключа нет нужного права","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"422":{"description":"Значение не прошло проверку","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["guests"],"security":[{"ApiKey":["guests:read"]}]}},"/v1/guests/{guest_id}":{"get":{"operationId":"guests_get","summary":"Получить гостя","description":"Один гость по id из выгрузки или поиска. Гость другого ресторана — 404.","parameters":[{"name":"guest_id","in":"path","required":true,"description":"Id гостя","schema":{"type":"string","examples":["gst_7Hq2kP9x"]}}],"responses":{"200":{"description":"Гость","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Guest"}}}},"400":{"description":"Неизвестный параметр","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"}}}}},"401":{"description":"Нет ключа или он не подходит","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"}}}}},"403":{"description":"У ключа нет нужного права","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["guests"],"security":[{"ApiKey":["guests:read"]}]}},"/v1/segments":{"get":{"operationId":"segments_list","summary":"Сегменты ресторана","description":"Сегменты с числом гостей и сколько получат SMS, если отправить сейчас. Порядок — по id.","parameters":[{"$ref":"#/components/parameters/CursorParams.limit"},{"$ref":"#/components/parameters/CursorParams.cursor"}],"responses":{"200":{"description":"Страница сегментов","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SegmentList"}}}},"400":{"description":"Неизвестный параметр или чужой курсор","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"},"invalid_cursor":{"$ref":"#/components/examples/invalid_cursor"}}}}},"401":{"description":"Нет ключа или он не подходит","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"}}}}},"403":{"description":"У ключа нет нужного права","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"}}}}},"422":{"description":"Значение не прошло проверку","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["segments"],"security":[{"ApiKey":["guests:read"]}]}},"/v1/segments/{segment_id}":{"get":{"operationId":"segments_get","summary":"Получить сегмент","description":"Один сегмент по id из списка сегментов или из `segment_ids` гостя. Число гостей и охват — на момент последнего пересчёта сегмента (`updated_at`).","parameters":[{"name":"segment_id","in":"path","required":true,"description":"Id сегмента","schema":{"type":"string","examples":["seg_at_risk"]}}],"responses":{"200":{"description":"Сегмент","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Segment"}}}},"400":{"description":"Неизвестный параметр","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"}}}}},"401":{"description":"Нет ключа или он не подходит","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"}}}}},"403":{"description":"У ключа нет нужного права","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["segments"],"security":[{"ApiKey":["guests:read"]}]}},"/v1/campaigns":{"post":{"operationId":"campaigns_create","summary":"Создать черновик SMS-рассылки","description":"Черновик по сегменту: в ответе прогноз аудитории и оценка стоимости. Ничего не отправляет —\nдля отправки `POST /v1/campaigns/{campaign_id}/send`. Причины 422 в `errors[]`:\n`unknown_segment`, `text_empty`, `text_too_long`, `scheduled_at_in_past`, `invalid_datetime`.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"201":{"description":"Черновик создан","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}}},"400":{"description":"Неизвестный параметр","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"}}}}},"401":{"description":"Нет ключа или он не подходит","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"}}}}},"403":{"description":"У ключа нет нужного права","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"}}}}},"409":{"description":"Первый запрос с этим ключом повтора ещё выполняется","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"idempotency_request_in_progress":{"$ref":"#/components/examples/idempotency_request_in_progress"}}}}},"422":{"description":"Поле не прошло проверку или ключ повтора занят","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"},"idempotency_key_reused":{"$ref":"#/components/examples/idempotency_key_reused"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignCreate"}}}},"security":[{"ApiKey":["campaigns:write"]}]}},"/v1/campaigns/{campaign_id}":{"get":{"operationId":"campaigns_get","summary":"Получить рассылку","description":"Статус, счётчики доставки и стоимость рассылки, созданной через API.","parameters":[{"$ref":"#/components/parameters/CampaignPath"}],"responses":{"200":{"description":"Рассылка","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}}},"400":{"description":"Неизвестный параметр","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"}}}}},"401":{"description":"Нет ключа или он не подходит","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"}}}}},"403":{"description":"У ключа нет нужного права","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["campaigns"],"security":[{"ApiKey":["campaigns:read"]}]}},"/v1/campaigns/{campaign_id}/send":{"post":{"operationId":"campaigns_send","summary":"Отправить рассылку","description":"Замораживает состав получателей на момент вызова и ставит рассылку в очередь: без\n`scheduled_at` SMS уходят в течение нескольких минут (`status: sending`), иначе\n`status: scheduled`. Подтверждения человеком нет. Тело пустое.\n\nЗаголовок `Idempotency-Key` обязателен: повтор с тем же ключом вернёт тот же ответ и вторую\nрассылку не создаст. После обрыва сети запрос можно смело повторить тем же ключом.","parameters":[{"$ref":"#/components/parameters/CampaignPath"},{"name":"Idempotency-Key","in":"header","required":true,"description":"Ключ повтора, например UUID; без него — 400 `idempotency_key_required`","schema":{"type":"string","examples":["0b7e3f52-8a61-4f2d-a3c9-5e8d7b6a1f20"]}}],"responses":{"200":{"description":"Рассылка поставлена в очередь","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}}},"400":{"description":"Кривой запрос или нет ключа повтора","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"},"idempotency_key_required":{"$ref":"#/components/examples/idempotency_key_required"}}}}},"401":{"description":"Нет ключа или он не подходит","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"}}}}},"403":{"description":"У ключа нет нужного права","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"409":{"description":"Рассылку нельзя отправить в её состоянии","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"campaign_already_sent":{"$ref":"#/components/examples/campaign_already_sent"},"empty_audience":{"$ref":"#/components/examples/empty_audience"},"idempotency_request_in_progress":{"$ref":"#/components/examples/idempotency_request_in_progress"}}}}},"422":{"description":"Поле не прошло проверку или ключ повтора занят","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"},"idempotency_key_reused":{"$ref":"#/components/examples/idempotency_key_reused"}}}}},"429":{"description":"Лимит запросов или отправок","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"},"send_limit_reached":{"$ref":"#/components/examples/send_limit_reached"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Отправка или зависимый сервис временно недоступны","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"sending_disabled":{"$ref":"#/components/examples/sending_disabled"},"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["campaigns"],"security":[{"ApiKey":["campaigns:write"]}]}},"/v1/campaigns/{campaign_id}/cancel":{"post":{"operationId":"campaigns_cancel","summary":"Отменить отправку","description":"Снимает запланированную рассылку до начала отправки и возвращает её в `draft`. Если SMS уже\nуходят или рассылка не запланирована — 409 `campaign_not_cancelable`.","parameters":[{"$ref":"#/components/parameters/CampaignPath"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Рассылка снова черновик","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}}},"400":{"description":"Неизвестный параметр","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"}}}}},"401":{"description":"Нет ключа или он не подходит","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"}}}}},"403":{"description":"У ключа нет нужного права","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"409":{"description":"Отменить нельзя","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"campaign_not_cancelable":{"$ref":"#/components/examples/campaign_not_cancelable"},"idempotency_request_in_progress":{"$ref":"#/components/examples/idempotency_request_in_progress"}}}}},"422":{"description":"Ключ повтора не подходит","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"},"idempotency_key_reused":{"$ref":"#/components/examples/idempotency_key_reused"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["campaigns"],"security":[{"ApiKey":["campaigns:write"]}]}},"/v1/events":{"get":{"operationId":"events_list","summary":"Журнал событий","description":"События рассылок по порядку создания — те же, что приходят на вебхук. Для сверки после\nпростоя приёмника передайте в `after` id последнего обработанного события.","parameters":[{"name":"after","in":"query","required":false,"description":"Id события, после которого отдавать","schema":{"type":"string","examples":["evt_2bX9kQ7m"]}},{"name":"type","in":"query","required":false,"description":"Только события этого типа","schema":{"type":"string","examples":["campaign.sent"]}},{"$ref":"#/components/parameters/CursorParams.limit"},{"$ref":"#/components/parameters/CursorParams.cursor"}],"responses":{"200":{"description":"Страница событий","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventList"}}}},"400":{"description":"Неизвестный параметр или чужой курсор","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"},"invalid_cursor":{"$ref":"#/components/examples/invalid_cursor"}}}}},"401":{"description":"Нет ключа или он не подходит","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"}}}}},"403":{"description":"У ключа нет нужного права","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"}}}}},"422":{"description":"Значение не прошло проверку","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["events"],"security":[{"ApiKey":["campaigns:read"]}]}},"/v1/loyalty/programs/{program_id}/orders/{order_reference}/quote":{"post":{"operationId":"loyalty_quote","summary":"Wallet quote","description":"One Wallet lifecycle with scoped server authentication. Provider order references remain stable; source aliases are explicit. Retries replay original receipts. Money is exact RUB; client categories, guest profile and tenant identifiers are forbidden. Quote precedes reserve/redeem; settlement requires its original quote and actual payment; cancel/refund never re-evaluate current rules.","parameters":[{"name":"program_id","in":"path","required":true,"description":"Program from the channel grant onboarding.","schema":{"type":"string","pattern":"^wpr_[0-9a-f]{32}$","examples":["wpr_12345678123456781234567812345678"]}},{"name":"order_reference","in":"path","required":true,"description":"Stable external order reference, not a guessed CRM bigint.","schema":{"type":"string","minLength":1,"maxLength":100,"examples":["provider-order-1"]}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Wallet receipt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletCheckoutResult"}}}},"400":{"description":"Invalid parameter or missing command key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"},"idempotency_key_required":{"$ref":"#/components/examples/idempotency_key_required"}}}}},"401":{"description":"Missing, revoked or expired credential","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"},"channel_key_expired":{"$ref":"#/components/examples/channel_key_expired"}}}}},"403":{"description":"Channel authorization refused","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"},"channel_grant_required":{"$ref":"#/components/examples/channel_grant_required"},"channel_scope_mismatch":{"$ref":"#/components/examples/channel_scope_mismatch"},"server_channel_required":{"$ref":"#/components/examples/server_channel_required"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"409":{"description":"Checkout conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"loyalty_conflict":{"$ref":"#/components/examples/loyalty_conflict"},"idempotency_conflict":{"$ref":"#/components/examples/idempotency_conflict"}}}}},"422":{"description":"Значение не прошло проверку","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["loyalty"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletQuoteRequest"}}}},"security":[{"ApiKey":["loyalty:write"]}]}},"/v1/loyalty/programs/{program_id}/orders/{order_reference}/redeem":{"post":{"operationId":"loyalty_redeem","summary":"Wallet redeem","description":"One Wallet lifecycle with scoped server authentication. Provider order references remain stable; source aliases are explicit. Retries replay original receipts. Money is exact RUB; client categories, guest profile and tenant identifiers are forbidden. Quote precedes reserve/redeem; settlement requires its original quote and actual payment; cancel/refund never re-evaluate current rules.","parameters":[{"name":"program_id","in":"path","required":true,"description":"Program from the channel grant onboarding.","schema":{"type":"string","pattern":"^wpr_[0-9a-f]{32}$","examples":["wpr_12345678123456781234567812345678"]}},{"name":"order_reference","in":"path","required":true,"description":"Stable external order reference, not a guessed CRM bigint.","schema":{"type":"string","minLength":1,"maxLength":100,"examples":["provider-order-1"]}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Wallet receipt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletCheckoutResult"}}}},"400":{"description":"Invalid parameter or missing command key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"},"idempotency_key_required":{"$ref":"#/components/examples/idempotency_key_required"}}}}},"401":{"description":"Missing, revoked or expired credential","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"},"channel_key_expired":{"$ref":"#/components/examples/channel_key_expired"}}}}},"403":{"description":"Channel authorization refused","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"},"channel_grant_required":{"$ref":"#/components/examples/channel_grant_required"},"channel_scope_mismatch":{"$ref":"#/components/examples/channel_scope_mismatch"},"server_channel_required":{"$ref":"#/components/examples/server_channel_required"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"409":{"description":"Checkout conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"loyalty_conflict":{"$ref":"#/components/examples/loyalty_conflict"},"idempotency_conflict":{"$ref":"#/components/examples/idempotency_conflict"}}}}},"422":{"description":"Значение не прошло проверку","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["loyalty"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletRedeemRequest"}}}},"security":[{"ApiKey":["loyalty:write"]}]}},"/v1/loyalty/programs/{program_id}/orders/{order_reference}/settle":{"post":{"operationId":"loyalty_settle","summary":"Wallet settle","description":"One Wallet lifecycle with scoped server authentication. Provider order references remain stable; source aliases are explicit. Retries replay original receipts. Money is exact RUB; client categories, guest profile and tenant identifiers are forbidden. Quote precedes reserve/redeem; settlement requires its original quote and actual payment; cancel/refund never re-evaluate current rules.","parameters":[{"name":"program_id","in":"path","required":true,"description":"Program from the channel grant onboarding.","schema":{"type":"string","pattern":"^wpr_[0-9a-f]{32}$","examples":["wpr_12345678123456781234567812345678"]}},{"name":"order_reference","in":"path","required":true,"description":"Stable external order reference, not a guessed CRM bigint.","schema":{"type":"string","minLength":1,"maxLength":100,"examples":["provider-order-1"]}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Wallet receipt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletCheckoutResult"}}}},"400":{"description":"Invalid parameter or missing command key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"},"idempotency_key_required":{"$ref":"#/components/examples/idempotency_key_required"}}}}},"401":{"description":"Missing, revoked or expired credential","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"},"channel_key_expired":{"$ref":"#/components/examples/channel_key_expired"}}}}},"403":{"description":"Channel authorization refused","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"},"channel_grant_required":{"$ref":"#/components/examples/channel_grant_required"},"channel_scope_mismatch":{"$ref":"#/components/examples/channel_scope_mismatch"},"server_channel_required":{"$ref":"#/components/examples/server_channel_required"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"409":{"description":"Checkout conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"loyalty_conflict":{"$ref":"#/components/examples/loyalty_conflict"},"idempotency_conflict":{"$ref":"#/components/examples/idempotency_conflict"}}}}},"422":{"description":"Значение не прошло проверку","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["loyalty"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletSettleRequest"}}}},"security":[{"ApiKey":["loyalty:write"]}]}},"/v1/loyalty/programs/{program_id}/orders/{order_reference}/cancel":{"post":{"operationId":"loyalty_cancel","summary":"Wallet cancel","description":"One Wallet lifecycle with scoped server authentication. Provider order references remain stable; source aliases are explicit. Retries replay original receipts. Money is exact RUB; client categories, guest profile and tenant identifiers are forbidden. Quote precedes reserve/redeem; settlement requires its original quote and actual payment; cancel/refund never re-evaluate current rules.","parameters":[{"name":"program_id","in":"path","required":true,"description":"Program from the channel grant onboarding.","schema":{"type":"string","pattern":"^wpr_[0-9a-f]{32}$","examples":["wpr_12345678123456781234567812345678"]}},{"name":"order_reference","in":"path","required":true,"description":"Stable external order reference, not a guessed CRM bigint.","schema":{"type":"string","minLength":1,"maxLength":100,"examples":["provider-order-1"]}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Wallet receipt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletCheckoutResult"}}}},"400":{"description":"Invalid parameter or missing command key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"},"idempotency_key_required":{"$ref":"#/components/examples/idempotency_key_required"}}}}},"401":{"description":"Missing, revoked or expired credential","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"},"channel_key_expired":{"$ref":"#/components/examples/channel_key_expired"}}}}},"403":{"description":"Channel authorization refused","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"},"channel_grant_required":{"$ref":"#/components/examples/channel_grant_required"},"channel_scope_mismatch":{"$ref":"#/components/examples/channel_scope_mismatch"},"server_channel_required":{"$ref":"#/components/examples/server_channel_required"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"409":{"description":"Checkout conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"loyalty_conflict":{"$ref":"#/components/examples/loyalty_conflict"},"idempotency_conflict":{"$ref":"#/components/examples/idempotency_conflict"}}}}},"422":{"description":"Значение не прошло проверку","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["loyalty"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletCancelRequest"}}}},"security":[{"ApiKey":["loyalty:write"]}]}},"/v1/loyalty/programs/{program_id}/orders/{order_reference}/refund":{"post":{"operationId":"loyalty_refund","summary":"Wallet refund","description":"One Wallet lifecycle with scoped server authentication. Provider order references remain stable; source aliases are explicit. Retries replay original receipts. Money is exact RUB; client categories, guest profile and tenant identifiers are forbidden. Quote precedes reserve/redeem; settlement requires its original quote and actual payment; cancel/refund never re-evaluate current rules.","parameters":[{"name":"program_id","in":"path","required":true,"description":"Program from the channel grant onboarding.","schema":{"type":"string","pattern":"^wpr_[0-9a-f]{32}$","examples":["wpr_12345678123456781234567812345678"]}},{"name":"order_reference","in":"path","required":true,"description":"Stable external order reference, not a guessed CRM bigint.","schema":{"type":"string","minLength":1,"maxLength":100,"examples":["provider-order-1"]}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Wallet receipt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletCheckoutResult"}}}},"400":{"description":"Invalid parameter or missing command key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"},"idempotency_key_required":{"$ref":"#/components/examples/idempotency_key_required"}}}}},"401":{"description":"Missing, revoked or expired credential","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"},"channel_key_expired":{"$ref":"#/components/examples/channel_key_expired"}}}}},"403":{"description":"Channel authorization refused","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"},"channel_grant_required":{"$ref":"#/components/examples/channel_grant_required"},"channel_scope_mismatch":{"$ref":"#/components/examples/channel_scope_mismatch"},"server_channel_required":{"$ref":"#/components/examples/server_channel_required"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"409":{"description":"Checkout conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"loyalty_conflict":{"$ref":"#/components/examples/loyalty_conflict"},"idempotency_conflict":{"$ref":"#/components/examples/idempotency_conflict"}}}}},"422":{"description":"Значение не прошло проверку","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["loyalty"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletRefundRequest"}}}},"security":[{"ApiKey":["loyalty:write"]}]}},"/v1/loyalty/programs/{program_id}/orders/{order_reference}/status":{"get":{"operationId":"loyalty_status","summary":"Wallet status","description":"One Wallet lifecycle with scoped server authentication. Provider order references remain stable; source aliases are explicit. Retries replay original receipts. Money is exact RUB; client categories, guest profile and tenant identifiers are forbidden. Quote precedes reserve/redeem; settlement requires its original quote and actual payment; cancel/refund never re-evaluate current rules.","parameters":[{"name":"program_id","in":"path","required":true,"description":"Program from the channel grant onboarding.","schema":{"type":"string","pattern":"^wpr_[0-9a-f]{32}$","examples":["wpr_12345678123456781234567812345678"]}},{"name":"order_reference","in":"path","required":true,"description":"Stable external order reference, not a guessed CRM bigint.","schema":{"type":"string","minLength":1,"maxLength":100,"examples":["provider-order-1"]}}],"responses":{"200":{"description":"Wallet receipt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletCheckoutResult"}}}},"400":{"description":"Invalid parameter or missing command key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"unknown_parameter":{"$ref":"#/components/examples/unknown_parameter"},"idempotency_key_required":{"$ref":"#/components/examples/idempotency_key_required"}}}}},"401":{"description":"Missing, revoked or expired credential","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"missing_api_key":{"$ref":"#/components/examples/missing_api_key"},"invalid_api_key":{"$ref":"#/components/examples/invalid_api_key"},"channel_key_expired":{"$ref":"#/components/examples/channel_key_expired"}}}}},"403":{"description":"Channel authorization refused","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"insufficient_scope":{"$ref":"#/components/examples/insufficient_scope"},"channel_grant_required":{"$ref":"#/components/examples/channel_grant_required"},"channel_scope_mismatch":{"$ref":"#/components/examples/channel_scope_mismatch"},"server_channel_required":{"$ref":"#/components/examples/server_channel_required"}}}}},"404":{"description":"Объекта нет или он другого ресторана","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"not_found":{"$ref":"#/components/examples/not_found"}}}}},"409":{"description":"Checkout conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"loyalty_conflict":{"$ref":"#/components/examples/loyalty_conflict"},"idempotency_conflict":{"$ref":"#/components/examples/idempotency_conflict"}}}}},"422":{"description":"Значение не прошло проверку","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"validation_error":{"$ref":"#/components/examples/validation_error"}}}}},"429":{"description":"Слишком много запросов","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"rate_limited":{"$ref":"#/components/examples/rate_limited"}}}}},"500":{"description":"Ошибка на нашей стороне","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"internal_error":{"$ref":"#/components/examples/internal_error"}}}}},"503":{"description":"Зависимый сервис временно недоступен","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"examples":{"upstream_unavailable":{"$ref":"#/components/examples/upstream_unavailable"}}}}}},"tags":["loyalty"],"security":[{"ApiKey":["loyalty:read"]}]}}},"webhooks":{"campaign_event":{"post":{"operationId":"webhooks_campaign_event","summary":"Событие рассылки на вебхук ресторана","description":"LazyCRM шлёт `POST` на адрес из кабинета, когда рассылка из API меняет состояние.\nОтветьте любым 2xx в течение 10 секунд; иначе будут повторы. Подпись проверяйте до\nразбора тела — алгоритм в описании раздела events.","parameters":[{"name":"webhook-id","in":"header","required":true,"description":"Id события, он же `id` в теле; по нему отбрасывайте повторы","schema":{"type":"string","examples":["evt_2bX9kQ7m"]}},{"name":"webhook-timestamp","in":"header","required":true,"description":"Момент отправки, секунды Unix; запрос старше 5 минут отклоняйте","schema":{"type":"string","examples":["1792921262"]}},{"name":"webhook-signature","in":"header","required":true,"description":"Подписи через пробел в виде `v1,<base64 HMAC-SHA256>`","schema":{"type":"string","examples":["v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4="]}}],"responses":{"200":{"description":"Событие принято; подойдёт любой 2xx"}},"tags":["events"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"http","scheme":"bearer","description":"Ключ ресторана в заголовке `Authorization: Bearer lcrm_live_…` (тестовый — `lcrm_test_…`).\nБез ключа — 401 `missing_api_key`, ключ отозван или неизвестен — 401 `invalid_api_key`,\nнет нужного права — 403 `insufficient_scope`."}},"parameters":{"CursorParams.limit":{"name":"limit","in":"query","required":false,"description":"Размер страницы, от 1 до 100","schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"examples":[100]}},"CursorParams.cursor":{"name":"cursor","in":"query","required":false,"description":"`next_cursor` предыдущей страницы; привязан к фильтрам запроса","schema":{"type":"string","examples":["c_eyJ2IjpbIjIwMjYtMTAtMTMiXX0"]}},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Ключ повтора, например UUID; повтор с тем же ключом вернёт сохранённый ответ","schema":{"type":"string","examples":["6f1c2b8e-1d4a-4c55-9b0e-0f6d1a2c3b4d"]}},"CampaignPath":{"name":"campaign_id","in":"path","required":true,"description":"Id рассылки","schema":{"type":"string","examples":["cmp_Q4n8Rt2v"]}}},"examples":{"unknown_parameter":{"summary":"Неизвестный query-параметр или поле тела (в т.ч. tenant_id); имя — в errors[].field","description":"Убрать параметр","value":{"type":"https://lazycrm.ru/docs/api/errors#unknown_parameter","code":"unknown_parameter","title":"Неизвестный параметр","status":400,"detail":"Параметр «tenant_id» не поддерживается этим методом.","errors":[{"field":"tenant_id","code":"unknown_parameter"}],"request_id":"req_8f2c1a9b"}},"invalid_cursor":{"summary":"Курсор битый или выдан для другого запроса","description":"Начать список заново без cursor","value":{"type":"https://lazycrm.ru/docs/api/errors#invalid_cursor","code":"invalid_cursor","title":"Курсор не подходит","status":400,"detail":"Курсор битый или выдан для другого запроса. Начните список заново без cursor.","request_id":"req_8f2c1a9b"}},"idempotency_key_required":{"summary":"Отправка без заголовка Idempotency-Key","description":"Добавить заголовок (UUID)","value":{"type":"https://lazycrm.ru/docs/api/errors#idempotency_key_required","code":"idempotency_key_required","title":"Нужен Idempotency-Key","status":400,"detail":"Передайте заголовок Idempotency-Key (например, UUID): по нему повтор не создаст вторую рассылку.","request_id":"req_8f2c1a9b"}},"missing_api_key":{"summary":"Нет заголовка Authorization","description":"Передать ключ","value":{"type":"https://lazycrm.ru/docs/api/errors#missing_api_key","code":"missing_api_key","title":"Нет ключа API","status":401,"detail":"Передайте ключ в заголовке Authorization: Bearer lcrm_live_…","request_id":"req_8f2c1a9b"}},"invalid_api_key":{"summary":"Ключ неизвестен или отозван","description":"Выпустить новый в кабинете","value":{"type":"https://lazycrm.ru/docs/api/errors#invalid_api_key","code":"invalid_api_key","title":"Ключ API не подходит","status":401,"detail":"Ключ не найден или отозван. Выпустите новый в кабинете: Интеграции → API.","request_id":"req_8f2c1a9b"}},"paid_feature_required":{"summary":"GET /v1/guests без платной опции «Выгрузка базы по API»","description":"Подключить опцию у менеджера или искать гостя через /v1/guests/lookup","value":{"type":"https://lazycrm.ru/docs/api/errors#paid_feature_required","code":"paid_feature_required","title":"Нужна платная опция","status":403,"detail":"Выгрузка базы по API — платная опция. Подключите её у менеджера LazyCRM; одного гостя можно найти без неё: GET /v1/guests/lookup?phone=…","request_id":"req_8f2c1a9b"}},"insufficient_scope":{"summary":"У ключа нет нужного права; detail называет его","description":"Выпустить ключ с полным доступом","value":{"type":"https://lazycrm.ru/docs/api/errors#insufficient_scope","code":"insufficient_scope","title":"У ключа нет нужного права","status":403,"detail":"У ключа нет права campaigns:write. Выпустите ключ с полным доступом в кабинете.","request_id":"req_8f2c1a9b"}},"not_found":{"summary":"Объекта нет или он другого ресторана","description":"Проверить id","value":{"type":"https://lazycrm.ru/docs/api/errors#not_found","code":"not_found","title":"Объект не найден","status":404,"detail":"Гостя gst_7Hq2kP9x нет в базе ресторана.","request_id":"req_8f2c1a9b"}},"campaign_already_sent":{"summary":"Отправка рассылки не в draft с новым ключом повтора","description":"Прочитать статус GET /v1/campaigns/{campaign_id}","value":{"type":"https://lazycrm.ru/docs/api/errors#campaign_already_sent","code":"campaign_already_sent","title":"Рассылка уже отправлена","status":409,"detail":"Рассылка уже отправлена или запланирована. Статус — GET /v1/campaigns/{id}.","request_id":"req_8f2c1a9b"}},"campaign_not_cancelable":{"summary":"Отправка уже началась, закончилась или рассылка не запланирована","description":"Ничего — рассылку уже не отменить","value":{"type":"https://lazycrm.ru/docs/api/errors#campaign_not_cancelable","code":"campaign_not_cancelable","title":"Рассылку уже нельзя отменить","status":409,"detail":"Отправка уже началась или закончилась — отменить нельзя.","request_id":"req_8f2c1a9b"}},"empty_audience":{"summary":"На момент отправки получателей 0","description":"Выбрать другой сегмент","value":{"type":"https://lazycrm.ru/docs/api/errors#empty_audience","code":"empty_audience","title":"Некому отправлять","status":409,"detail":"В сегменте сейчас некому отправить SMS. Выберите другой сегмент.","request_id":"req_8f2c1a9b"}},"idempotency_request_in_progress":{"summary":"Первый запрос с этим ключом повтора ещё выполняется","description":"Повторить через секунду","value":{"type":"https://lazycrm.ru/docs/api/errors#idempotency_request_in_progress","code":"idempotency_request_in_progress","title":"Запрос с этим Idempotency-Key ещё выполняется","status":409,"detail":"Первый запрос с этим ключом ещё выполняется. Повторите через секунду.","request_id":"req_8f2c1a9b"}},"validation_error":{"summary":"Значение не прошло проверку; причины в errors[]: unknown_segment, text_empty, text_too_long, scheduled_at_in_past, invalid_phone, invalid_datetime, out_of_range, required","description":"Исправить поле из errors[].field","value":{"type":"https://lazycrm.ru/docs/api/errors#validation_error","code":"validation_error","title":"Поле не прошло проверку","status":422,"detail":"Телефон должен быть в формате +7XXXXXXXXXX, получено «8 900 12».","errors":[{"field":"phone","code":"invalid_phone"}],"request_id":"req_8f2c1a9b"}},"idempotency_key_reused":{"summary":"Тот же ключ повтора с другим запросом","description":"Новый ключ на новый запрос","value":{"type":"https://lazycrm.ru/docs/api/errors#idempotency_key_reused","code":"idempotency_key_reused","title":"Idempotency-Key уже использован с другим запросом","status":422,"detail":"Этот Idempotency-Key уже использован с другим запросом. Для нового запроса нужен новый ключ.","request_id":"req_8f2c1a9b"}},"rate_limited":{"summary":"Больше 600 запросов в минуту на ключ; Retry-After","description":"Подождать Retry-After секунд","value":{"type":"https://lazycrm.ru/docs/api/errors#rate_limited","code":"rate_limited","title":"Слишком много запросов","status":429,"detail":"Не больше 600 запросов в минуту на ключ. Повторите через 12 с.","request_id":"req_8f2c1a9b"}},"send_limit_reached":{"summary":"20 отправок за сутки уже было; Retry-After до полуночи ресторана","description":"Отправить завтра","value":{"type":"https://lazycrm.ru/docs/api/errors#send_limit_reached","code":"send_limit_reached","title":"Лимит отправок на сегодня исчерпан","status":429,"detail":"Не больше 20 отправок в сутки на ресторан. Следующая — после полуночи по времени ресторана.","request_id":"req_8f2c1a9b"}},"sending_disabled":{"summary":"Отправка SMS на платформе временно выключена; Retry-After","description":"Повторить позже тем же ключом повтора","value":{"type":"https://lazycrm.ru/docs/api/errors#sending_disabled","code":"sending_disabled","title":"Отправка временно выключена","status":503,"detail":"Отправка SMS временно выключена. Повторите позже с тем же Idempotency-Key.","request_id":"req_8f2c1a9b"}},"upstream_unavailable":{"summary":"Сервис или SMS-шлюз временно недоступен; Retry-After","description":"Повторить позже","value":{"type":"https://lazycrm.ru/docs/api/errors#upstream_unavailable","code":"upstream_unavailable","title":"Сервис временно недоступен","status":503,"detail":"Лимиты запросов временно недоступны. Повторите запрос через минуту.","request_id":"req_8f2c1a9b"}},"internal_error":{"summary":"Ошибка на нашей стороне","description":"Повторить запрос; не проходит — написать в поддержку с request_id","value":{"type":"https://lazycrm.ru/docs/api/errors#internal_error","code":"internal_error","title":"Внутренняя ошибка","status":500,"detail":"Внутренняя ошибка. Сообщите request_id в поддержку LazyCRM.","request_id":"req_8f2c1a9b"}},"channel_key_expired":{"summary":"Checkout boundary returned channel_key_expired","description":"Inspect the scoped channel and original operation receipt before retrying.","value":{"type":"https://lazycrm.ru/docs/api/errors#channel_key_expired","code":"channel_key_expired","title":"Wallet channel_key_expired","status":401,"detail":"Wallet channel_key_expired","request_id":"req_8f2c1a9b"}},"channel_grant_required":{"summary":"Checkout boundary returned channel_grant_required","description":"Inspect the scoped channel and original operation receipt before retrying.","value":{"type":"https://lazycrm.ru/docs/api/errors#channel_grant_required","code":"channel_grant_required","title":"Wallet channel_grant_required","status":403,"detail":"Wallet channel_grant_required","request_id":"req_8f2c1a9b"}},"channel_scope_mismatch":{"summary":"Checkout boundary returned channel_scope_mismatch","description":"Inspect the scoped channel and original operation receipt before retrying.","value":{"type":"https://lazycrm.ru/docs/api/errors#channel_scope_mismatch","code":"channel_scope_mismatch","title":"Wallet channel_scope_mismatch","status":403,"detail":"Wallet channel_scope_mismatch","request_id":"req_8f2c1a9b"}},"server_channel_required":{"summary":"Checkout boundary returned server_channel_required","description":"Inspect the scoped channel and original operation receipt before retrying.","value":{"type":"https://lazycrm.ru/docs/api/errors#server_channel_required","code":"server_channel_required","title":"Wallet server_channel_required","status":403,"detail":"Wallet server_channel_required","request_id":"req_8f2c1a9b"}},"loyalty_conflict":{"summary":"Checkout boundary returned loyalty_conflict","description":"Inspect the scoped channel and original operation receipt before retrying.","value":{"type":"https://lazycrm.ru/docs/api/errors#loyalty_conflict","code":"loyalty_conflict","title":"Wallet loyalty_conflict","status":409,"detail":"Wallet loyalty_conflict","request_id":"req_8f2c1a9b"}},"idempotency_conflict":{"summary":"Checkout boundary returned idempotency_conflict","description":"Inspect the scoped channel and original operation receipt before retrying.","value":{"type":"https://lazycrm.ru/docs/api/errors#idempotency_conflict","code":"idempotency_conflict","title":"Wallet idempotency_conflict","status":409,"detail":"Wallet idempotency_conflict","request_id":"req_8f2c1a9b"}}},"schemas":{"Money":{"type":"object","description":"Сумма строкой с двумя знаками и валюта рядом — не float и не копейки","additionalProperties":false,"required":["amount","currency"],"properties":{"amount":{"type":"string","pattern":"^-?\\d+\\.\\d{2}$","description":"Сумма с двумя знаками после точки","examples":["15400.00"]},"currency":{"type":"string","description":"Валюта, ISO 4217","examples":["RUB"],"const":"RUB"}}},"Guest":{"type":"object","description":"Гость ресторана","additionalProperties":false,"required":["id","object","phone","name","orders_count","total_spent","first_order_date","last_order_date","segment_ids","sms_opt_out","updated_at"],"properties":{"id":{"type":"string","pattern":"^gst_[0-9A-Za-z]{1,11}$","description":"Неизменный непрозрачный id","examples":["gst_7Hq2kP9x"]},"object":{"type":"string","description":"Тип объекта","examples":["guest"],"const":"guest"},"phone":{"type":["string","null"],"description":"Телефон в E.164; null — телефон неизвестен","examples":["+79001234567"]},"name":{"type":["string","null"],"description":"Имя из CRM ресторана; null — нет","examples":["Анна"]},"orders_count":{"type":"integer","description":"Заказов за всю историю","examples":[14]},"total_spent":{"$ref":"#/components/schemas/Money","description":"Сумма заказов за всю историю"},"first_order_date":{"type":["string","null"],"format":"date","description":"Бизнес-дата первого заказа в поясе ресторана","examples":["2025-03-02"]},"last_order_date":{"type":["string","null"],"format":"date","description":"Бизнес-дата последнего заказа в поясе ресторана","examples":["2026-10-12"]},"segment_ids":{"type":"array","items":{"type":"string","pattern":"^seg_[a-z0-9_]{1,40}$"},"description":"Сегменты, в которых гость сейчас; может быть пустым","examples":[["seg_loyal"]]},"sms_opt_out":{"type":"boolean","description":"Гость отписался от SMS — рассылки до него не дойдут","examples":[false]},"updated_at":{"type":"string","format":"date-time","description":"UTC; меняется при любом изменении полей выше","examples":["2026-10-13T21:04:11Z"]}}},"GuestList":{"type":"object","description":"Страница гостей","additionalProperties":false,"required":["data","has_more","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Guest"},"description":"Гости страницы"},"has_more":{"type":"boolean","description":"Есть ли следующая страница","examples":[true]},"next_cursor":{"type":["string","null"],"description":"Передайте в `cursor`, чтобы получить следующую страницу; null — страниц больше нет","examples":["c_eyJ2IjpbIjIwMjYtMTAtMTMiXX0"]}}},"ProblemFieldError":{"type":"object","description":"Причина ошибки в конкретном поле","additionalProperties":false,"required":["field","code"],"properties":{"field":{"type":"string","description":"Имя поля, параметра или заголовка","examples":["phone"]},"code":{"type":"string","description":"Машинная причина, например `invalid_phone` или `unknown_parameter`","examples":["invalid_phone"]}}},"Problem":{"type":"object","description":"Ошибка в формате RFC 9457 (`application/problem+json`)","additionalProperties":false,"required":["type","code","title","status","detail","request_id"],"properties":{"type":{"type":"string","description":"Ссылка на описание кода ошибки","examples":["https://lazycrm.ru/docs/api/errors#not_found"]},"code":{"type":"string","description":"Машинный код — контракт, по нему ветвится клиент; список — примеры ответов и /docs/api/errors","examples":["not_found"]},"title":{"type":"string","description":"Короткий заголовок для человека; может уточняться","examples":["Объект не найден"]},"status":{"type":"integer","description":"HTTP-статус ответа","examples":[404]},"detail":{"type":"string","description":"Что не так и как исправить; текст может меняться","examples":["Гостя gst_7Hq2kP9x нет в базе ресторана."]},"errors":{"type":"array","items":{"$ref":"#/components/schemas/ProblemFieldError"},"description":"Причины по полям — при 400 `unknown_parameter` и 422 `validation_error`"},"request_id":{"type":"string","pattern":"^req_","description":"Id запроса, он же заголовок `Request-Id`; назовите его поддержке","examples":["req_8f2c1a9b"]}}},"Segment":{"type":"object","description":"Сегмент гостей ресторана","additionalProperties":false,"required":["id","object","name","description","kind","guests_count","reachable_count","updated_at"],"properties":{"id":{"type":"string","pattern":"^seg_[a-z0-9_]{1,40}$","description":"Неизменный id","examples":["seg_at_risk"]},"object":{"type":"string","description":"Тип объекта","examples":["segment"],"const":"segment"},"name":{"type":"string","description":"Название, как в кабинете; может меняться","examples":["Под угрозой ухода"]},"description":{"type":["string","null"],"description":"Пояснение для человека","examples":["Ходили часто, пропали ~45 дней"]},"kind":{"type":"string","description":"`rfm` — стандартный сегмент по давности, частоте и чеку; `custom` — настроенный рестораном","examples":["rfm"],"x-extensible-enum":["rfm","custom"]},"guests_count":{"type":"integer","description":"Гостей в сегменте сейчас","examples":[1830]},"reachable_count":{"type":"integer","description":"Сколько получат SMS, если отправить сейчас — без отписавшихся, без телефона и без попавших под частотный лимит ресторана","examples":[1412]},"updated_at":{"type":"string","format":"date-time","description":"Когда сегмент пересчитан, UTC","examples":["2026-10-14T03:00:00Z"]}}},"SegmentList":{"type":"object","description":"Страница сегментов","additionalProperties":false,"required":["data","has_more","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Segment"},"description":"Сегменты страницы"},"has_more":{"type":"boolean","description":"Есть ли следующая страница","examples":[false]},"next_cursor":{"type":["string","null"],"description":"Передайте в `cursor`, чтобы получить следующую страницу; null — страниц больше нет","examples":[null]}}},"CampaignStats":{"type":"object","description":"Счётчики получателей; числа только растут","additionalProperties":false,"required":["sent","delivered","failed"],"properties":{"sent":{"type":"integer","description":"Отправлено SMS","examples":[1405]},"delivered":{"type":"integer","description":"Доставлено","examples":[1371]},"failed":{"type":"integer","description":"Не доставлено или не отправлено","examples":[34]}}},"Campaign":{"type":"object","description":"SMS-рассылка по сегменту, созданная через API","additionalProperties":false,"required":["id","object","name","status","segment_id","text","scheduled_at","audience_count","sms_parts","cost","cost_final","stats","failure_reason","created_at","sent_at","updated_at"],"properties":{"id":{"type":"string","pattern":"^cmp_[0-9A-Za-z]{1,11}$","description":"Неизменный id","examples":["cmp_Q4n8Rt2v"]},"object":{"type":"string","description":"Тип объекта","examples":["campaign"],"const":"campaign"},"name":{"type":"string","description":"Название; если не передано при создании — по сегменту","examples":["Вернуть уснувших"]},"status":{"type":"string","description":"`draft` → `scheduled` → `sending` → `sent`; `failed` — отправка не состоялась","examples":["draft"],"x-extensible-enum":["draft","scheduled","sending","sent","failed"]},"segment_id":{"type":"string","pattern":"^seg_[a-z0-9_]{1,40}$","description":"Аудитория — id сегмента","examples":["seg_at_risk"]},"text":{"type":"string","description":"Текст SMS","examples":["Анна, скучаем! −15% по коду BACK15"]},"scheduled_at":{"type":["string","null"],"format":"date-time","description":"Когда отправить, UTC; null — сразу после send","examples":["2026-10-15T08:00:00Z"]},"audience_count":{"type":"integer","description":"Сколько получат. В `draft` — прогноз; после send — замороженный состав","examples":[1412]},"sms_parts":{"type":"integer","description":"Частей SMS на одного получателя — зависит от длины и алфавита текста","examples":[2]},"cost":{"anyOf":[{"$ref":"#/components/schemas/Money"},{"type":"null"}],"description":"В `draft` и `scheduled` — оценка, после `sent` — по факту шлюза"},"cost_final":{"type":"boolean","description":"true — `cost` фактический и больше не изменится","examples":[false]},"stats":{"anyOf":[{"$ref":"#/components/schemas/CampaignStats"},{"type":"null"}],"description":"Счётчики получателей; null до начала отправки"},"failure_reason":{"type":["string","null"],"description":"Почему отправка не состоялась; только при `failed`, иначе null","examples":[null],"x-extensible-enum":["sending_disabled","empty_audience","gateway_rejected"]},"created_at":{"type":"string","format":"date-time","description":"Когда создана, UTC","examples":["2026-10-14T09:30:00Z"]},"sent_at":{"type":["string","null"],"format":"date-time","description":"Когда отправка завершена, UTC; null — ещё не завершена","examples":[null]},"updated_at":{"type":"string","format":"date-time","description":"Последнее изменение, UTC","examples":["2026-10-14T09:30:00Z"]}}},"CampaignCreate":{"type":"object","description":"Черновик рассылки","additionalProperties":false,"required":["segment_id","text"],"properties":{"segment_id":{"type":"string","pattern":"^seg_[a-z0-9_]{1,40}$","description":"Аудитория — id сегмента из `GET /v1/segments`","examples":["seg_at_risk"]},"text":{"type":"string","description":"Текст SMS, от 1 до 1000 символов","examples":["Анна, скучаем! −15% по коду BACK15"]},"name":{"type":["string","null"],"maxLength":255,"description":"Название рассылки; не передано — по сегменту","examples":["Вернуть уснувших"]},"scheduled_at":{"type":["string","null"],"format":"date-time","description":"Когда отправить, RFC 3339 с поясом; null — сразу после send","examples":["2026-10-15T08:00:00Z"]}}},"EventData":{"type":"object","description":"Объект события — за его данными идите GET'ом","additionalProperties":false,"required":["id"],"properties":{"id":{"type":"string","pattern":"^cmp_[0-9A-Za-z]{1,11}$","description":"Id рассылки","examples":["cmp_Q4n8Rt2v"]}}},"Event":{"type":"object","description":"Событие рассылки — тонкое, за данными GET по `data.id`","additionalProperties":false,"required":["id","type","created_at","data"],"properties":{"id":{"type":"string","pattern":"^evt_[0-9A-Za-z]{1,11}$","description":"Id события; повторная доставка приходит с тем же id","examples":["evt_2bX9kQ7m"]},"type":{"type":"string","description":"`campaign.sending` — отправка началась; `campaign.sent` — завершена (доставка досчитывается ещё сутки); `campaign.failed` — не состоялась","examples":["campaign.sent"],"x-extensible-enum":["campaign.sending","campaign.sent","campaign.failed"]},"created_at":{"type":"string","format":"date-time","description":"Когда событие произошло, UTC","examples":["2026-10-14T09:41:02Z"]},"data":{"$ref":"#/components/schemas/EventData","description":"Объект, с которым произошло событие"}}},"EventList":{"type":"object","description":"Страница событий","additionalProperties":false,"required":["data","has_more","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"},"description":"События страницы"},"has_more":{"type":"boolean","description":"Есть ли следующая страница","examples":[false]},"next_cursor":{"type":["string","null"],"description":"Передайте в `cursor`, чтобы получить следующую страницу; null — страниц больше нет","examples":[null]}}},"WalletQuantity":{"type":"string","description":"Exact quantity, never a JSON float.","pattern":"^(0|[1-9][0-9]{0,6})(\\.[0-9]{1,3})?$"},"WalletGiftAllocation":{"type":"object","description":"Member-bound gift allocation, separate from cash and bonus tender.","additionalProperties":false,"required":["instrument_id","gift_amount"],"properties":{"instrument_id":{"type":"string","pattern":"^wgi_[0-9a-f]{32}$","description":"Original instrument in this program, never authority by ID alone.","examples":["wgi_12345678123456781234567812345678"]},"gift_amount":{"$ref":"#/components/schemas/Money","description":"Exact allocated gift tender in program currency."}}},"WalletQuotedLine":{"type":"object","description":"Program-scoped WalletQuotedLine.","additionalProperties":false,"required":["line_id","product_id","quantity","pre_discount_sum","discount","net_sum","money_planned","bonus_planned","redeem_limit"],"properties":{"line_id":{"type":"string","pattern":"^wli_[A-Za-z0-9_-]{1,171}$","description":"Reversible external source line reference, prefixed wli.","examples":["wli_YQ"]},"product_id":{"type":"string","pattern":"^wpi_[A-Za-z0-9_-]{1,171}$","description":"Reversible source catalog reference, prefixed wpi.","examples":["wpi_YQ"]},"quantity":{"$ref":"#/components/schemas/WalletQuantity","description":"Original sold quantity as an exact decimal string.","examples":["1.000"]},"pre_discount_sum":{"$ref":"#/components/schemas/Money","description":"Exact pre discount sum from the original server receipt."},"gift_planned":{"$ref":"#/components/schemas/Money","description":"Frozen planned gift tender."},"gift_allocations":{"type":"array","items":{"$ref":"#/components/schemas/WalletGiftAllocation"},"description":"Frozen instrument allocation."},"discount":{"$ref":"#/components/schemas/Money","description":"Exact discount from the original server receipt."},"net_sum":{"$ref":"#/components/schemas/Money","description":"Exact net sum from the original server receipt."},"money_planned":{"$ref":"#/components/schemas/Money","description":"Exact money planned from the original server receipt."},"bonus_planned":{"$ref":"#/components/schemas/Money","description":"Exact bonus planned from the original server receipt."},"redeem_limit":{"$ref":"#/components/schemas/Money","description":"Exact redeem limit from the original server receipt."}}},"WalletQuote":{"type":"object","description":"Program-scoped WalletQuote.","additionalProperties":false,"required":["id","sale_id","account_id","rule_revision","engine_version","snapshot_version","lines","discount","net_total","money_planned","bonus_planned"],"properties":{"request_key":{"type":"string","minLength":1,"maxLength":200,"description":"Original caller command key; use it to correlate an uncertain quote response.","examples":["reference-quote-command-1"]},"gift_planned":{"$ref":"#/components/schemas/Money","description":"Frozen total planned gift tender."},"id":{"type":"string","pattern":"^wqt_[0-9a-f]{32}$","description":"Exact id from the original server receipt.","examples":["wqt_12345678123456781234567812345678"]},"sale_id":{"type":"string","pattern":"^wsl_[0-9a-f]{32}$","description":"Canonical sale shared only by explicit verified aliases.","examples":["wsl_12345678123456781234567812345678"]},"account_id":{"type":"string","pattern":"^wac_[0-9a-f]{32}$","description":"Stable existing financial account; never a newly allocated public identity.","examples":["wac_12345678123456781234567812345678"]},"rule_revision":{"type":"integer","description":"Exact rule revision from the original server receipt.","examples":[1]},"engine_version":{"type":"string","description":"Exact engine version from the original server receipt.","examples":["wallet-rules-1"]},"snapshot_version":{"type":"integer","description":"Exact snapshot version from the original server receipt.","examples":[1]},"lines":{"type":"array","items":{"$ref":"#/components/schemas/WalletQuotedLine"},"description":"Original cart lines or frozen actual line allocations."},"discount":{"$ref":"#/components/schemas/Money","description":"Exact discount from the original server receipt."},"net_total":{"$ref":"#/components/schemas/Money","description":"Exact net total from the original server receipt."},"money_planned":{"$ref":"#/components/schemas/Money","description":"Exact money planned from the original server receipt."},"bonus_planned":{"$ref":"#/components/schemas/Money","description":"Exact bonus planned from the original server receipt."}}},"WalletGiftPayment":{"type":"object","description":"Actual original-instrument payment fact from the reviewed server adapter.","additionalProperties":false,"required":["instrument_id","gift_amount","payment_reference","payment_type_id"],"properties":{"instrument_id":{"type":"string","pattern":"^wgi_[0-9a-f]{32}$","description":"Original instrument in this program, never authority by ID alone.","examples":["wgi_12345678123456781234567812345678"]},"gift_amount":{"$ref":"#/components/schemas/Money","description":"Exact allocated gift tender in program currency."},"payment_reference":{"type":"string","minLength":1,"maxLength":128,"description":"Stable original instrument payment transaction reference.","examples":["reference-gift-payment-1"]},"payment_type_id":{"type":"string","pattern":"^wpt_[0-9a-f]{32}$","description":"Operator-reviewed gift payment mapping, distinct from cash and bonus.","examples":["wpt_12345678123456781234567812345678"]}}},"WalletSettledLine":{"type":"object","description":"Program-scoped WalletSettledLine.","additionalProperties":false,"required":["line_id","product_id","quantity","pre_discount_sum","discount","net_sum","money_paid","bonus_paid","earned"],"properties":{"line_id":{"type":"string","pattern":"^wli_[A-Za-z0-9_-]{1,171}$","description":"Reversible external source line reference, prefixed wli.","examples":["wli_YQ"]},"product_id":{"type":"string","pattern":"^wpi_[A-Za-z0-9_-]{1,171}$","description":"Reversible source catalog reference, prefixed wpi.","examples":["wpi_YQ"]},"quantity":{"$ref":"#/components/schemas/WalletQuantity","description":"Original sold quantity as an exact decimal string.","examples":["1.000"]},"pre_discount_sum":{"$ref":"#/components/schemas/Money","description":"Exact pre discount sum from the original server receipt."},"gift_paid":{"$ref":"#/components/schemas/Money","description":"Actual gift amount, distinct from bonus paid."},"gift_allocations":{"type":"array","items":{"$ref":"#/components/schemas/WalletGiftAllocation"},"maxItems":8,"description":"Actual per-instrument line allocation."},"discount":{"$ref":"#/components/schemas/Money","description":"Exact discount from the original server receipt."},"net_sum":{"$ref":"#/components/schemas/Money","description":"Exact net sum from the original server receipt."},"money_paid":{"$ref":"#/components/schemas/Money","description":"Exact money paid from the original server receipt."},"bonus_paid":{"$ref":"#/components/schemas/Money","description":"Exact bonus paid from the original server receipt."},"earned":{"$ref":"#/components/schemas/Money","description":"Exact earned from the original server receipt."}}},"WalletSettlement":{"type":"object","description":"Program-scoped WalletSettlement.","additionalProperties":false,"required":["id","quote_id","lines"],"properties":{"gift_payments":{"type":"array","items":{"$ref":"#/components/schemas/WalletGiftPayment"},"description":"Original confirmed instrument payment facts, with no presentation secrets."},"id":{"type":"string","pattern":"^wst_[0-9a-f]{32}$","description":"Exact id from the original server receipt.","examples":["wst_12345678123456781234567812345678"]},"quote_id":{"type":"string","pattern":"^wqt_[0-9a-f]{32}$","description":"Exact quote id from the original server receipt.","examples":["wqt_12345678123456781234567812345678"]},"lines":{"type":"array","items":{"$ref":"#/components/schemas/WalletSettledLine"},"description":"Original cart lines or frozen actual line allocations."}}},"WalletRefundReceipt":{"type":"object","description":"Program-scoped WalletRefundReceipt.","additionalProperties":false,"required":["id","source_refund_reference"],"properties":{"id":{"type":"string","pattern":"^wrf_[0-9a-f]{32}$","description":"Exact id from the original server receipt.","examples":["wrf_12345678123456781234567812345678"]},"source_refund_reference":{"type":"string","description":"Stable provider refund reference unique within the program.","examples":["provider-reference-1"]}}},"WalletCheckoutResult":{"type":"object","description":"Program-scoped WalletCheckoutResult.","additionalProperties":false,"required":["state","balance"],"properties":{"state":{"type":"string","description":"Derived lifecycle state: quoted, redeemed, cancelled, settled or refunded.","examples":["quoted"],"x-extensible-enum":["quoted","redeemed","cancelled","settled","refunded"]},"balance":{"$ref":"#/components/schemas/Money","description":"Own-ledger balance, with program currency."},"quote":{"$ref":"#/components/schemas/WalletQuote","description":"Server-owned frozen quote."},"settlement":{"$ref":"#/components/schemas/WalletSettlement","description":"Immutable original settlement."},"earned":{"$ref":"#/components/schemas/Money","description":"Exact earned from the original server receipt."},"discount":{"$ref":"#/components/schemas/Money","description":"Exact discount from the original server receipt."},"accrual_reversed":{"$ref":"#/components/schemas/Money","description":"Exact accrual reversed from the original server receipt."},"redeem_returned":{"$ref":"#/components/schemas/Money","description":"Exact redeem returned from the original server receipt."},"money_returned":{"$ref":"#/components/schemas/Money","description":"Exact money returned from the original server receipt."},"refunds":{"type":"array","items":{"$ref":"#/components/schemas/WalletRefundReceipt"},"description":"Immutable refund receipts; no reevaluation of current rules."}}},"WalletPresentedCode":{"type":"string","description":"Private code consumed by the scoped resolver, never persisted raw.","minLength":1,"maxLength":100,"format":"password","examples":["SYNTHETIC-PUBLIC-CODE"],"writeOnly":true},"WalletBasketLine":{"type":"object","description":"Program-scoped WalletBasketLine.","additionalProperties":false,"required":["line_id","product_id","quantity","pre_discount_sum"],"properties":{"line_id":{"type":"string","pattern":"^wli_[A-Za-z0-9_-]{1,171}$","description":"Reversible external source line reference, prefixed wli.","examples":["wli_YQ"]},"product_id":{"type":"string","pattern":"^wpi_[A-Za-z0-9_-]{1,171}$","description":"Reversible source catalog reference, prefixed wpi.","examples":["wpi_YQ"]},"quantity":{"$ref":"#/components/schemas/WalletQuantity","description":"Original sold quantity as an exact decimal string.","examples":["1.000"]},"pre_discount_sum":{"$ref":"#/components/schemas/Money","description":"Exact pre discount sum from the original server receipt."}}},"WalletQuoteRequest":{"type":"object","description":"Program-scoped WalletQuoteRequest.","additionalProperties":false,"required":["account_id","lines","order_total","planned_bonus","payment_mode"],"properties":{"presented_codes":{"type":"array","items":{"$ref":"#/components/schemas/WalletPresentedCode"},"maxItems":1,"description":"Transient code presentation; only scoped HMAC enters idempotency/journal."},"presented_rewards":{"type":"array","items":{"type":"string","pattern":"^wri_[0-9a-f]{32}$"},"maxItems":1,"description":"Existing member-bound issued item IDs; no creation or issuance here.","examples":[["wri_12345678123456781234567812345678"]]},"gift_tenders":{"type":"array","items":{"$ref":"#/components/schemas/WalletGiftAllocation"},"maxItems":8,"description":"Planned gift tender without its private presentation proof."},"fulfillment":{"type":"string","minLength":1,"maxLength":40,"description":"Actual fulfillment asserted by the approved server adapter; never inferred from its technical channel.","examples":["hall"],"x-extensible-enum":["hall","courier","pickup"]},"account_id":{"type":"string","pattern":"^wac_[0-9a-f]{32}$","description":"Stable existing financial account; never a newly allocated public identity.","examples":["wac_12345678123456781234567812345678"]},"lines":{"type":"array","items":{"$ref":"#/components/schemas/WalletBasketLine"},"description":"Original cart lines or frozen actual line allocations."},"order_total":{"$ref":"#/components/schemas/Money","description":"Exact order total from the original server receipt."},"planned_bonus":{"$ref":"#/components/schemas/Money","description":"Exact planned bonus from the original server receipt."},"payment_mode":{"type":"string","description":"Exact payment mode from the original server receipt.","examples":["money_only"],"x-extensible-enum":["money_only","with_bonuses","bonuses_only"]},"expected_previous_quote_id":{"type":"string","pattern":"^wqt_[0-9a-f]{32}$","description":"Exact expected previous quote id from the original server receipt.","examples":["wqt_12345678123456781234567812345678"]}}},"WalletGiftProof":{"type":"object","description":"Transient proof, presented only while reserving the frozen gift plan.","additionalProperties":false,"required":["instrument_id","code"],"properties":{"instrument_id":{"type":"string","pattern":"^wgi_[0-9a-f]{32}$","description":"Member-bound instrument from the original quote.","examples":["wgi_12345678123456781234567812345678"]},"code":{"type":"string","minLength":1,"maxLength":200,"format":"password","description":"Private presented code; never returned or stored in a durable checkout payload.","examples":["synthetic-presented-gift-code"],"writeOnly":true}}},"WalletRedeemRequest":{"type":"object","description":"New reserve proof is transient and is not a settlement field.","additionalProperties":false,"required":["quote_id","payment_reference"],"properties":{"current_fulfillment":{"type":"string","minLength":1,"maxLength":40,"description":"Fresh fulfillment before a new reserve; must match the frozen provider quote.","examples":["hall"],"x-extensible-enum":["hall","courier","pickup"]},"quote_id":{"type":"string","pattern":"^wqt_[0-9a-f]{32}$","description":"Exact quote id from the original server receipt.","examples":["wqt_12345678123456781234567812345678"]},"payment_reference":{"type":"string","description":"Stable provider payment transaction reference.","examples":["provider-reference-1"]},"gift_proofs":{"type":"array","items":{"$ref":"#/components/schemas/WalletGiftProof"},"maxItems":8,"description":"Private member-bound proofs for exactly the frozen instruments."}}},"WalletPaymentLine":{"type":"object","description":"Program-scoped WalletPaymentLine.","additionalProperties":false,"required":["line_id","product_id","quantity","pre_discount_sum","discount","net_sum","money_paid","bonus_paid"],"properties":{"line_id":{"type":"string","pattern":"^wli_[A-Za-z0-9_-]{1,171}$","description":"Reversible external source line reference, prefixed wli.","examples":["wli_YQ"]},"product_id":{"type":"string","pattern":"^wpi_[A-Za-z0-9_-]{1,171}$","description":"Reversible source catalog reference, prefixed wpi.","examples":["wpi_YQ"]},"quantity":{"$ref":"#/components/schemas/WalletQuantity","description":"Original sold quantity as an exact decimal string.","examples":["1.000"]},"pre_discount_sum":{"$ref":"#/components/schemas/Money","description":"Exact pre discount sum from the original server receipt."},"gift_paid":{"$ref":"#/components/schemas/Money","description":"Actual gift amount, distinct from bonus paid."},"gift_allocations":{"type":"array","items":{"$ref":"#/components/schemas/WalletGiftAllocation"},"maxItems":8,"description":"Actual per-instrument line allocation."},"discount":{"$ref":"#/components/schemas/Money","description":"Exact discount from the original server receipt."},"net_sum":{"$ref":"#/components/schemas/Money","description":"Exact net sum from the original server receipt."},"money_paid":{"$ref":"#/components/schemas/Money","description":"Exact money paid from the original server receipt."},"bonus_paid":{"$ref":"#/components/schemas/Money","description":"Exact bonus paid from the original server receipt."}}},"WalletSettleRequest":{"type":"object","description":"Program-scoped WalletSettleRequest.","additionalProperties":false,"required":["quote_id","payment_reference","lines","order_total","money_paid","bonus_paid"],"properties":{"current_fulfillment":{"type":"string","minLength":1,"maxLength":40,"description":"Fresh fulfillment before a new reserve; must match the frozen provider quote.","examples":["hall"],"x-extensible-enum":["hall","courier","pickup"]},"quote_id":{"type":"string","pattern":"^wqt_[0-9a-f]{32}$","description":"Exact quote id from the original server receipt.","examples":["wqt_12345678123456781234567812345678"]},"payment_reference":{"type":"string","description":"Stable provider payment transaction reference.","examples":["provider-reference-1"]},"gift_paid":{"$ref":"#/components/schemas/Money","description":"Exact actual gift tender total."},"gift_allocations":{"type":"array","items":{"$ref":"#/components/schemas/WalletGiftAllocation"},"maxItems":8,"description":"Exact actual gift instrument totals."},"gift_payments":{"type":"array","items":{"$ref":"#/components/schemas/WalletGiftPayment"},"maxItems":8,"description":"Original actual gift transactions; mapping must match frozen operator approval."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/WalletPaymentLine"},"description":"Original cart lines or frozen actual line allocations."},"order_total":{"$ref":"#/components/schemas/Money","description":"Exact order total from the original server receipt."},"money_paid":{"$ref":"#/components/schemas/Money","description":"Exact money paid from the original server receipt."},"bonus_paid":{"$ref":"#/components/schemas/Money","description":"Exact bonus paid from the original server receipt."}}},"WalletCancelRequest":{"type":"object","description":"Program-scoped WalletCancelRequest.","additionalProperties":false,"required":["quote_id"],"properties":{"quote_id":{"type":"string","pattern":"^wqt_[0-9a-f]{32}$","description":"Exact quote id from the original server receipt.","examples":["wqt_12345678123456781234567812345678"]}}},"WalletRefundLine":{"type":"object","description":"Program-scoped WalletRefundLine.","additionalProperties":false,"required":["line_id","returned_quantity","money_returned","bonus_returned"],"properties":{"gift_returned":{"type":"array","items":{"$ref":"#/components/schemas/WalletGiftAllocation"},"maxItems":8,"description":"Cumulative return to original instruments, never substituted with cash or bonus."},"line_id":{"type":"string","pattern":"^wli_[A-Za-z0-9_-]{1,171}$","description":"Reversible external source line reference, prefixed wli.","examples":["wli_YQ"]},"returned_quantity":{"$ref":"#/components/schemas/WalletQuantity","description":"Cumulative actual returned quantity, zero for untouched lines.","examples":["1.000"]},"money_returned":{"$ref":"#/components/schemas/Money","description":"Exact money returned from the original server receipt."},"bonus_returned":{"$ref":"#/components/schemas/Money","description":"Exact bonus returned from the original server receipt."}}},"WalletRefundRequest":{"type":"object","description":"Program-scoped WalletRefundRequest.","additionalProperties":false,"required":["source_refund_reference","lines"],"properties":{"source_refund_reference":{"type":"string","description":"Stable provider refund reference unique within the program.","examples":["provider-reference-1"]},"lines":{"type":"array","items":{"$ref":"#/components/schemas/WalletRefundLine"},"description":"Original cart lines or frozen actual line allocations."}}}}}}