Public APIOwner API

Waitly Developer Docs

Интеграции для владельцев: меню, столы, плагины маркетплейса, usage-метрики.

База: https://api.moonlauncher.org/api/public-api/ · Доки: https://docs.moonlauncher.org · Формат: JSON, заголовок X-Api-Key

1. Быстрый старт

1. В кабинете владельца откройте «API ключи», создайте ключ и сразу скопируйте секрет — больше он не покажется.

curl https://api.moonlauncher.org/api/public-api/menu/my-cafe/
curl -H "X-Api-Key: wsk_..." \
  "https://api.moonlauncher.org/api/public-api/owner/restaurants/"

2. Ключ с scope OWNER видит все точки владельца; для действий с точкой передавайте ?restaurant_id=.

2. Авторизация

CredentialФорматЧто это
X-Api-Keywsk_...Новый секрет. В БД хранится только SHA-256, показывается 1 раз при создании/ротации
X-Api-Keywl_...Legacy-ключ (обратная совместимость)
wpk_...Публичный ID, не credential. Светить можно, вызывать API им нельзя (401)

Управление ключами (создание, ротация, отзыв) — только по JWT владельца в кабинете: /api/public-api/keys/. По ApiKey управлять ключами нельзя.

3. Скоупы ключа

ScopeКонтур
RESTAURANTОдна точка. Без явных permissions — read-only (owner.read, plugins.read, menu.read)
OWNERВсе точки владельца и его сети. 1 владелец = 1 API-контур
CHAINОдна сеть (все её точки)

Тонкие права — permissions: owner.read owner.write plugins.read plugins.write menu.read orders.read orders.write reports.read keys.read. Пусто = legacy full.

Чужое недоступно по построению: restaurant_id вне контура даёт 403. Если ключ покрывает много точек и restaurant_id не передан — 400 со списком точек.

4. Эндпоинты

Без ключа

МетодПутьОтвет
GET/api/public-api/menu/<slug>/Категории + блюда: id, name, description, price, image, cooking_time
GET/api/public-api/tables/<slug>/Столы: id, number, is_occupied

По X-Api-Key

МетодПутьПравоОтвет
GET/api/public-api/owner/restaurants/owner.readid, name, slug, chain_id всех точек контура
GET/api/public-api/owner/plugins/<slug>/status/?restaurant_id=plugins.readenabled, status, egress плагина в точке
POST/api/public-api/owner/plugins/<slug>/action/plugins.write{"restaurant_id","action","data"} → результат action. Спец-действия: approve_host revoke_host fetch
GET/api/public-api/owner/usage/keys.readtoday_requests, quota_left, errors_today, avg_ms, top_endpoints, abuse_score, blocked_until
GET/api/public-api/owner/menu/menu.readПолное меню: категории, блюда (без cost_price), модификаторы, стоп-лист
GET/api/public-api/owner/orders/?status=&limit=orders.readЧеки: номер, статус, стол, сумма, официант. limit до 200
GET/api/public-api/owner/orders/<id>/orders.readЧек с позициями (блюдо, qty, цена, notes, гость, модификаторы)
GET/api/public-api/owner/tables/live/owner.readСтолы: занятость, активный чек, активная сессия
GET/api/public-api/owner/bookings/?date_from=&status=owner.readБрони (по умолчанию предстоящие)
GET/api/public-api/owner/reports/summary/?days=reports.readВыручка, чеки, средний чек, по статусам (days 1–90)
GET/api/public-api/owner/inventory/inventory.readОстатки: ингредиент, ед., qty, минимум, флаг is_low
GET/api/public-api/owner/reviews/owner.readОценки гостей с комментариями
GET/api/public-api/owner/staff/staff.readСотрудники: только имя и роль (без PIN/телефонов)
POST/api/public-api/owner/orders/create/orders.write{"restaurant_id","table_id|table_number","items":[{"dish_id","qty"}]} + idempotency по client_request_id. Стол занят → 409
POST/api/public-api/owner/orders/<id>/add-items/orders.writeДозаказ в активный чек
POST/api/public-api/owner/orders/<id>/status/orders.writeКухонная цепочка по ALLOWED_TRANSITIONS. PAID/AWAITING_PAYMENT → только с money opt-in (раздел 8)
POST/api/public-api/owner/bookings/create/bookings.writeБронь с проверкой вместимости и пересечений → 409 при конфликте
POST/api/public-api/owner/bookings/<id>/cancel/bookings.writeОтмена активной брони
{
  "restaurant_id": "3fa85f64-...",
  "action": "details",
  "data": { "order_id": "3fa85f64-..." }
}

Generic webhooks выключены: POST /api/public-api/webhook/<provider>/ всегда отвечает 410.

5. Плагины маркетплейса

Модуль должен быть установлен и включён в точке, иначе 404 not installed / 400 module disabled. Неизвестный action — 400 unknown action.

SlugActionsЧто делает
offline-possync_batchПачка до 200 офлайн-чеков, идемпотентность по client_request_id
fraud-radardetailsАнтифрод: репринты, дозаказы после счёта, paid-cancelled
guest-checkassign_guest, merge_ordersСчёт по гостям, склейка дозаказов
discount-guarddry_runЧестный просчёт корзины без двойных скидок
staff-ratingaccrue_bonusРейтинг официантов, начисление бонуса
cost-trend— (только status)Динамика цен поставок, задетые техкарты
gift-cardsissue, redeem, set_modeПодарочные карты
chain-syncpush_menu, push_promosКопирование меню/акций между точками сети
courier-deskassignНазначение курьера на доставку, SLA
receipt-designersave_template, previewШаблон чека, превью по живому заказу
scales-barcodeslookup, set_barcode, price_tags, weight_priceШтрихкоды весового товара
course-serviceset_course, order_coursesКурсы подачи блюд
feedback-pushnotifyПуш в TG о низкой оценке гостя
booking-reportremind_listБрони, no-show, список напоминаний

6. Лимиты и ошибки

Дефолт: 60 req/min на ключ (скользящее окно), daily_quota: 0 = без лимита. Каждый запрос пишется в лог (endpoint, method, status, ms, IP).

КодКогда
401Нет X-Api-Key, неверный/протухший ключ, передан wpk_ вместо секрета
403scope denied (нет права), IP not allowed, точка вне контура, временный блок за абьюз
400Нужен restaurant_id (ключ покрывает много точек), плохой action/data
404Нет плагина/модуля/заказа
429rate limit exceeded / daily quota exceeded
409TABLE_HAS_ACTIVE_ORDER (второй активный чек на стол — откройте существующий), DISH_ON_STOP_LIST, BOOKING_CONFLICT (слот пересекается)
403MONEY_OPS_EXCLUDED — PAID/AWAITING_PAYMENT через паблик ставить нельзя
400ILLEGAL_TRANSITION — статус вне ALLOWED_TRANSITIONS

Антиабьюз: серия из 10 ошибок подряд — блок 15 минут, серия из 5 рейтлимитов — блок 1 час. Успешный запрос сбрасывает счётчик. Метрики — в owner/usage/.

7. Управление ключами

Только по JWT владельца (Authorization: Bearer ...), endpoints /api/public-api/keys/:

ВызовЭффект
POST /keys/Создать. Секрет wsk_... в ответе один раз, дальше только wpk_...
POST /keys/<id>/rotate/Новый секрет, public_id стабилен, блокировки сняты
POST /keys/<id>/revoke/Kill-switch: мгновенно гасит ключ
GET /keys/<id>/usage/today_requests, quota_left, errors_today, avg_ms, top_endpoints

Опции ключа: expires_at, ip_allowlist (пусто = любые IP), rate_limit_per_minute, daily_quota. Утечка/подозрение — сразу revoke + rotate.

8. Деньги и согласия владельца

Скоупы payments.read (журнал операций) и payments.write (касса) — опасные: выключены всегда, даже у старых full-ключей. Без них денежные вызовы отвечают 403 DANGEROUS_SCOPE_DISABLED.

Как включить (только владелец, в кабинете)

1. POST /api/public-api/keys/<id>/dangerous/request/ {"scope"} — вернёт предупреждение «Вы уверены, что хотите включить … для посторонних API-сервисов?», текст условий и слово-подтверждение.

2. POST /api/public-api/keys/<id>/dangerous/confirm/ {"scope","confirm_text":"Accept","legal_accepted":true,"legal_version":"..."} — включает. Кто/когда/IP/версия условий пишутся в аудит. Выключить в любой момент: POST .../dangerous/revoke/ — доступ гаснет мгновенно.

Денежные эндпоинты

МетодПутьПравоОтвет
GET/api/public-api/owner/payments/?status=&limit=payments.readЖурнал операций: сумма, комиссия Waitly, провайдер, статус. Токены карт не отдаются никогда
POST/api/public-api/owner/orders/<id>/pay/ {"method":"CASH"}orders.write + payments.writeПриём наличных по поданному чеку (DELIVERED → PAID). Только CASH: без эквайринга, без фискального чека — фискализация остаётся на физической кассе

Онлайн-оплаты, привязка карт и сплит-выплаты через паблик недоступны в принципе.