REST API Conventions

Base URL

{gateway}/api/v1/{service-prefix}/...

Формат ответов

Успех

{
  "data": { ... },
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 150
  }
}

Ошибка

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Human-readable message",
    "details": [
      { "field": "email", "message": "Invalid format" }
    ]
  }
}

HTTP-статусы

КодКогда
200Успешный GET, PATCH, PUT
201Успешный POST (создание)
204Успешный DELETE
400Ошибка валидации
401Не авторизован
403Нет прав (роль не позволяет)
404Ресурс не найден
409Конфликт (дубликат и т.п.)
422Бизнес-логика не позволяет
500Внутренняя ошибка

Пагинация

Query-параметры: ?page=1&per_page=20

Ответ содержит meta с page, per_page, total.

Фильтрация

GET /catalog/products?category_id=uuid&store_id=uuid&is_active=true

Сортировка

GET /catalog/products?sort=name&order=asc

Авторизация

Authorization: Bearer <JWT>

JWT содержит: sub (user_id), role, franchise_id, store_ids, legal_entity_id.

Допустимые значения role (Исправлено в BR 1.4.2): admin_franchise, admin_franchisee, manager, cashier.

Мультитенантность

  • franchise_id из JWT — автоматическая фильтрация данных
  • admin_franchisee видит только данные своих store_ids и legal_entity_id
  • manager видит только данные своей ТТ
  • cashier работает в контексте одной ТТ (из PIN-авторизации)

Naming

  • URL: kebab-case (/legal-entities, /tech-cards)
  • JSON: snake_case (legal_entity_id, created_at)
  • Коллекции: множественное число (/products, /stores)