API: обзор
Один REST API обслуживает мобильное приложение (Android и iOS), сайт и админку. Здесь описаны общие соглашения, а список ресурсов приведён в разделе API: ресурсы.
Проект API в разработке. Контракт может меняться до версии v1. Источником истины будет спецификация OpenAPI: из неё генерируются клиенты для Kotlin Multiplatform и TypeScript.
Базовый адрес
https://api.mardikor.tj/v1
Только HTTPS. Версия входит в путь. Несовместимые изменения выходят только в новой версии.
Авторизация
- Клиент получает одноразовый код:
POST /v1/auth/otp/requestс номером телефона. - Подтверждает его:
POST /v1/auth/otp/verify, получает пару токенов. - Либо входит через
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.
Права доступа
Все изменяющие операции на сервере проверяют владельца ресурса, роль пользователя и допустимость перехода состояния. Клиент не должен полагаться на то, что скрытая кнопка защищает действие.