# API для разработчиков: Общий формат запросов и ответов

Этот файл описывает не конкретные операции, а общий wire-контракт всего API сервера.

Здесь зафиксировано:

- как выглядит любой запрос;
- как выглядит любой успешный ответ;
- как выглядит любой ответ с ошибкой;
- какие поля являются обязательными для всех операций;
- как клиент должен интерпретировать `status`, `ok` и `payload`.

Логика простая: сначала клиент и сервер договариваются о едином формате конверта, и только потом в остальных документах уже описываются конкретные методы и их поля.

## 1. Общий формат запроса

Все запросы по WebSocket используют один и тот же JSON-конверт:

```json
{
  "op": "OperationName",
  "requestId": "req-001",
  "payload": {
  }
}
```

### Поля

- `op` — имя операции.
- `requestId` — клиентский идентификатор запроса.
- `payload` — объект параметров операции.

---

## 2. Общий формат успешного ответа

```json
{
  "op": "OperationName",
  "requestId": "req-001",
  "status": 200,
  "ok": true,
  "payload": {
  }
}
```

---

## 3. Общий формат ответа с ошибкой

```json
{
  "op": "OperationName",
  "requestId": "req-001",
  "status": 400,
  "ok": false,
  "error": "BAD_REQUEST",
  "message": "Human readable description",
  "payload": {
  }
}
```

---

## 4. Обязательные правила

- Сервер возвращает `op` в каждом ответе.
- Сервер возвращает `requestId` в каждом ответе без изменений.
- Сервер возвращает `status` в каждом ответе.
- Сервер возвращает `ok` в каждом ответе.
- Сервер всегда возвращает `payload` как объект.
- Даже при отсутствии данных сервер возвращает `payload: {}`.
- `ok` находится на верхнем уровне ответа, а не внутри `payload`.

---

## 5. Правило интерпретации

Источник истины — `status`.

- если `status` в диапазоне `200..299`, то ответ успешный и `ok` должен быть `true`;
- если `status` вне диапазона `200..299`, то ответ ошибочный и `ok` должен быть `false`.

Запрещённые состояния:

- `status = 200` и `ok = false`;
- `status = 400` и `ok = true`.

---

## 6. Общие правила формата

- Все строки подписи и challenge собираются в UTF-8.
- Временные метки передаются как Unix time в миллисекундах.
- Бинарные поля передаются строками Base64.
- При ошибке `error` — это машинный код причины.
- При ошибке `message` — человекочитаемое описание причины.

---

## 7. Общие коды ошибок

Ниже перечислены коды ошибок, которые не привязаны к одной конкретной операции и могут встречаться в разных местах API.

- `400 / EMPTY_JSON` — клиент отправил пустое или полностью отсутствующее JSON-сообщение.
- `400 / NO_OP` — в корневом объекте не передано поле `op`.
- `400 / UNKNOWN_OP` — сервер не знает такую операцию.
- `400 / NO_PAYLOAD` — в корневом объекте отсутствует `payload`.
- `400 / BAD_PAYLOAD` — `payload` передан, но это не JSON-объект.
- `400 / BAD_REQUEST_FORMAT` — JSON-конверт формально валиден, но поля операции не удалось распарсить в ожидаемый формат.
- `500 / INTERNAL_HANDLER_ERROR` — в handler конкретной операции случилась непредвиденная серверная ошибка.
- `500 / INTERNAL_ERROR` — произошла внутренняя ошибка на уровне общего JSON-процессора или другого серверного слоя.

Общее правило для dev/test этапа:

- `message` в таких ошибках должен быть коротким, но полезным;
- по возможности сервер добавляет тип исключения и краткую деталь причины;
- это сделано для упрощения интеграционных тестов и отладки;
- позже для production этот уровень детализации может быть уменьшен.

---

## 8. Короткое резюме

- Запросы всегда идут как `op + requestId + payload`.
- Ответы всегда идут как `op + requestId + status + ok + payload`.
- Ошибки всегда возвращают `ok: false`, `error`, `message`, `payload: {}`.
