# API conventions и поведенческий контракт

## Паспорт документа

- Статус документа: living standard
- Актуально на: 29 марта 2026 года
- Владелец: backend/platform-команда
- Пересмотр: при изменении API-контракта, auth-flow, error-модели или handoff-процесса
- Область применения: общие правила HTTP API поверх живого OpenAPI-контракта Qadam
- Связанные документы:
  - [Карта API-маршрутов](./api-routes.md)
  - [Change Log для frontend-команды](../frontend/frontend-change-log.md)
  - [Памятка для frontend-команды](../frontend/frontend-handoff.md)

## Цель документа

OpenAPI остаётся машиночитаемым источником истины по endpoint и схемам. Этот документ фиксирует общие соглашения API-поведения: auth, error model, backward compatibility и ожидания для клиентов.

## 1. Источники истины

По приоритету:

1. runtime OpenAPI: `https://qadam.2fab.app/api/openapi.json`
2. versioned artifact: `qadam-core/apps/api/openapi/openapi.json`
3. обзорная карта: [api-routes.md](./api-routes.md)
4. frontend handoff и frontend changelog

Если человекочитаемый документ расходится с OpenAPI, contract source of truth — OpenAPI.

## 2. Base prefix и versioning

- Канонический base prefix: `/api/v1`
- Swagger UI публикуется на `/api/docs`
- OpenAPI JSON публикуется на `/api/openapi.json`

Новые клиентские интеграции не должны проектироваться на неверсированные пути.

## 3. Auth conventions

### Web

- Каноническая модель — `httpOnly` cookies
- access cookie: `qadam_at`
- refresh cookie: `qadam_rt`
- основной browser-flow: login -> `auth/me` -> refresh -> logout

### Non-web clients

- Допускается Bearer token в `Authorization` header
- Это дополнительный transport mode, а не замена web cookie model

## 4. Success responses

У API нет одного глобального envelope вида `{ data }`. Реальный response shape определяется OpenAPI и конкретным контроллером.

Практическое правило:

- не предполагать единый envelope по всему API;
- читать response types только из OpenAPI/generated contract;
- если endpoint возвращает объект-обёртку вроде `{ profile }`, `{ addresses }`, `{ user, seller }`, это часть контракта, а не “временный слой”.

## 5. Error model

Доменные ошибки сериализуются в общий формат:

```json
{
  "statusCode": 409,
  "errorCode": "EMAIL_TAKEN",
  "message": "..."
}
```

Базовые правила:

- `errorCode` — канонический идентификатор для UI и интеграций;
- `message` — человекочитаемое описание;
- точная карта кодов ошибок живёт в shared constants и domain filter;
- для field-level UI нельзя ориентироваться только на текст `message`.

## 6. Наиболее важные error codes

### Auth

- `INVALID_CREDENTIALS`
- `TOKEN_EXPIRED`
- `REFRESH_TOKEN_REVOKED`
- `ACCOUNT_BLOCKED`
- `ACCOUNT_UNDER_REVIEW`
- `PHONE_TAKEN`
- `EMAIL_TAKEN`

### Buyer

- `BUYER_NOT_FOUND`
- `BUYER_ALREADY_EXISTS`
- `CHILD_NOT_FOUND`
- `MAX_CHILDREN_REACHED`
- `MAX_INTERESTS_EXCEEDED`

### Seller

- `SELLER_NOT_FOUND`
- `SELLER_TYPE_IMMUTABLE`
- `MAX_ADDRESSES_REACHED`
- `CANNOT_DELETE_ONLY_ADDRESS`
- `TELEGRAM_ALREADY_BOUND`

### Uploads

- `FILE_TOO_LARGE`
- `INVALID_FORMAT`
- `IMAGE_TOO_SMALL`

## 7. Backward compatibility

- Legacy aliases могут существовать, но не считаются каноническим контрактом для новой разработки.
- Примеры: `PUT /me/profile`, `PUT /seller/profile`, старый `POST /auth/register`.
- Новые UI и интеграции должны строиться только на канонических маршрутах.
- Любой backend package, который вводит новый canonical path или меняет старый, обязан обновлять [frontend-change-log.md](../frontend/frontend-change-log.md).

## 8. Pagination, filters, search params

- Query params считаются частью transport contract.
- Пустые и неиспользуемые параметры не должны передаваться “на всякий случай”.
- Для публичного каталога canonical behavior нужно брать из OpenAPI и реального backend implementation, а не из старых frontend допущений.
- Любой change package, который меняет query/filter semantics, обязан обновлять `api-routes.md` и frontend changelog.

## 9. Upload conventions

- Image upload endpoint: `POST /api/v1/upload/image`
- Transport: `multipart/form-data`
- Имя поля файла: `file`
- Backend возвращает `url` как готовый публичный URL до загруженного объекта
- При `local` driver это обычно `/uploads/images/<fileName>`
- При `s3`/CDN driver это может быть абсолютный URL на bucket/CDN
- Клиент не должен самостоятельно придумывать storage URL в обход backend response

## 10. Временные и датовые правила

- Для API и тестов canonical timezone — UTC
- Все новые transport timestamps должны передаваться как ISO-8601 строки
- Серверные и тестовые сценарии, чувствительные ко времени, должны проверяться в `TZ=UTC`

## 11. Правило для frontend-команды

- Не поддерживать ручные transport types там, где endpoint уже описан в OpenAPI
- Не обходить generated contract layer без документированной причины
- Любой contract drift решается через backend artifact, а не локальным исправлением типов во frontend
