API: обзор

Один REST API обслуживает мобильное приложение (Android и iOS), сайт и админку. Здесь описаны общие соглашения, а список ресурсов приведён в разделе API: ресурсы.

Проект API в разработке. Контракт может меняться до версии v1. Источником истины будет спецификация OpenAPI: из неё генерируются клиенты для Kotlin Multiplatform и TypeScript.

Базовый адрес

https://api.mardikor.tj/v1

Только HTTPS. Версия входит в путь. Несовместимые изменения выходят только в новой версии.

Авторизация

  1. Клиент получает одноразовый код: POST /v1/auth/otp/request с номером телефона.
  2. Подтверждает его: POST /v1/auth/otp/verify, получает пару токенов.
  3. Либо входит через POST /v1/auth/oauth/google или /apple. Номер телефона всё равно нужно привязать до публикации, отклика или раскрытия контактов.
Authorization: Bearer <access_token>

Access-токен живёт 15 минут, refresh-токен 30 дней и одноразовый: при обновлении через POST /v1/auth/refresh выдаётся новая пара, старый refresh отзывается.

Форматы

  • JSON в UTF-8, поля в snake_case.
  • Даты в ISO-8601 UTC: 2026-11-12T08:00:00Z.
  • Деньги — целое число дирамов (1 сомони = 100 дирамов): "budget": 25000 означает 250 сомони.
  • Идентификаторы — непрозрачные строки (UUID). Не стройте на их формате логику.
  • Язык ответа задаётся заголовком Accept-Language: tg или ru.

Пагинация

Списки используют курсор, а не номер страницы: так лента не дублирует элементы, когда появляются новые.

GET /v1/jobs?category=tiles&limit=20&cursor=eyJpZCI6...

{
  "items": [ ... ],
  "next_cursor": "eyJpZCI6...",   // null, если дальше пусто
  "has_more": true
}

Идемпотентность

При слабом интернете клиент может повторить запрос. Чтобы не создать два заказа или два отклика, команды создания и смены статуса принимают заголовок:

Idempotency-Key: 6f1c2a9e-3b7d-4c8e-9a51-0d2e7f4b8c13

Повтор с тем же ключом в течение 24 часов вернёт тот же результат, не выполняя действие заново.

Ошибки

Формат Problem Details (RFC 9457):

HTTP/1.1 409 Conflict
Content-Type: application/problem+json

{
  "type": "https://mardikor.tj/docs/api/errors#invalid-transition",
  "title": "Недопустимый переход заказа",
  "status": 409,
  "code": "invalid_transition",
  "detail": "Заказ уже назначен другому исполнителю.",
  "request_id": "req_01HF..."
}
СтатусКогда
400Некорректный запрос, поле errors со списком полей
401Нет токена или он истёк
403Нет прав (не владелец, не та роль, телефон не подтверждён)
404Ресурс не найден или скрыт
409Конфликт состояния, например недопустимый переход заказа
422Данные корректны по формату, но нарушают бизнес-правило
429Слишком много запросов, см. заголовок Retry-After

Ограничения

  • Запрос SMS-кода: не чаще 1 раза в минуту и 5 раз в час на номер.
  • Остальные запросы ограничиваются по пользователю и IP. При превышении ответ 429.

Права доступа

Все изменяющие операции на сервере проверяют владельца ресурса, роль пользователя и допустимость перехода состояния. Клиент не должен полагаться на то, что скрытая кнопка защищает действие.