# Внутреннее API Felmee

## Общий формат ответа

Все REST-эндпоинты возвращают ответ в едином формате:

```json
{
  "success": true,
  "message": "...",
  "error_code": null,
  "data": {}
}
```

Где:
- `success` - флаг успешного выполнения запроса
- `message` - человекочитаемое описание результата
- `error_code` - машинный код ошибки; для успешных ответов всегда `null`
- `data` - полезная нагрузка; может быть объектом, массивом или `null`

## Возможные значения `error_code`

- `invalid_body` - тело запроса не прошло валидацию или нарушает бизнес-правила
- `unauthorized` - отсутствует или невалиден JWT / refresh token
- `not_found` - ресурс не найден
- `conflict` - запрос корректен по форме, но конфликтует с текущим состоянием ресурса
- `object_modified_by_another_thread` - объект был изменён другой параллельной операцией; клиент должен перечитать актуальное состояние и повторить запрос
- `action_too_late` - действие запрещено по бизнес-правилу минимального времени до начала события
- `too_many_request` - превышен лимит запросов
- `code_is_gone` - одноразовый код истёк или больше не действителен
- `forbidden` - у пользователя нет доступа к ресурсу
- `geocoding_failed` - адрес услуги или мастера передан, но backend не смог получить для него координаты
- `invalid_captcha` - captcha отсутствует, невалидна или не прошла проверку


Version: 0.0.5

## Servers

Продакшен
```
https://felmee.com
```

Локально / Dev (модуль мессенджера)
```
http://localhost:8080
```

## Security

### bearerAuth

Authorization: Bearer ${JWT}

Type: http
Scheme: bearer
Bearer Format: JWT

### jwtCookie

Cookie: JWT=${token}

Type: apiKey
In: cookie
Name: JWT

### refreshTokenHeader

RT: Bearer <refresh_token>

Type: apiKey
In: header
Name: RT

### refreshTokenCookie

Cookie: RT=<refresh_token>

Type: apiKey
In: cookie
Name: RT

## Download OpenAPI description

[Внутреннее API Felmee](https://docs.felmee.com/_bundle/OPENAPI.yaml)

## Аутентификация

Эндпоинты для авторизации, привязки email и обновления токенов.

## Captcha

Для эндпоинтов, где требуется captcha, клиент должен передать одноразовый токен
в заголовке:
`captcha-token: <token>`

**Yandex SmartCaptcha site key:** `ysc1_Vnah2iqYd6r7kQsOKKstEMxLp4GSd6zHfiU6gTvL0f84838e`

## Интеграция SmartCaptcha

1. Подключите скрипт SmartCaptcha на клиенте
2. Отрендерите виджет в контейнере
3. Получите одноразовый token после успешной проверки
4. Передайте token в API в заголовке `captcha-token`

Документация Yandex SmartCaptcha:
https://yandex.cloud/ru/docs/smartcaptcha/operations/advanced-method

## WebSocket

Для подключения к WebSocket клиенту сначала нужно получить JWT через REST-эндпоинты
аутентификации.

Дальше подключение выполняется так:
1. Открыть SockJS/WebSocket соединение на endpoint `/ws`
2. Выполнить STOMP CONNECT
3. Передать JWT в STOMP-заголовке `Authorization`

Формат заголовка:
`Authorization: Bearer <JWT>`

После успешного подключения клиент может отправлять сообщения в STOMP destinations
модуля мессенджера, например:
- `/app/chat.send`
- `/app/chat.read`
- `/app/chat.reaction`

И подписываться на пользовательские очереди:
- `/user/queue/chat.delivered`
- `/user/queue/chat.new_message`
- `/user/queue/chat.update_status_request`
- `/user/queue/chat.read_message`
- `/user/queue/chat.reaction`
- `/user/queue/errors`


### Отправить OTP на телефон

 - [POST /api/auth/phone/send/otp](https://docs.felmee.com/openapi/autentifikaciya/paths/~1api~1auth~1phone~1send~1otp/post.md): Отправляет одноразовый код подтверждения на указанный номер телефона.

Требования:
- обязателен заголовок captcha-token

### Войти по номеру телефона и OTP

 - [POST /api/auth/phone/login](https://docs.felmee.com/openapi/autentifikaciya/paths/~1api~1auth~1phone~1login/post.md): Авторизует пользователя по номеру телефона и OTP-коду.

Дополнительно клиент обязан передать type_account - ожидаемый тип аккаунта.
Допустимые значения: CLIENT, MASTER, SALON.

В успешном ответе возвращает пару токенов:
- JWT - access token
- RT - refresh token

### Войти по email и OTP

 - [POST /api/auth/email/login](https://docs.felmee.com/openapi/autentifikaciya/paths/~1api~1auth~1email~1login/post.md): Авторизует уже существующего пользователя по email и OTP-коду.

Регистрация по email здесь не выполняется. Клиент обязан передать type_account -
тип аккаунта, в который выполняется вход. Допустимые значения: CLIENT, MASTER, SALON.

В успешном ответе возвращает пару токенов:
- JWT - access token
- RT - refresh token

### Изменить телефон текущего пользователя

 - [POST /api/auth/phone/edit](https://docs.felmee.com/openapi/autentifikaciya/paths/~1api~1auth~1phone~1edit/post.md): Подтверждает новый номер телефона по OTP-коду и изменяет его у текущего пользователя.

Требования:
- требуется JWT
- новый номер не должен быть уже зарегистрирован

### Отправить OTP на email

 - [POST /api/auth/email/send/otp](https://docs.felmee.com/openapi/autentifikaciya/paths/~1api~1auth~1email~1send~1otp/post.md): Отправляет OTP-код на указанный email.

Особенности:
- JWT не требуется
- email может быть уже привязан к аккаунту
- обязателен заголовок captcha-token

### Привязать email к текущему пользователю

 - [PUT /api/auth/email/add](https://docs.felmee.com/openapi/autentifikaciya/paths/~1api~1auth~1email~1add/put.md): Подтверждает email по OTP-коду и привязывает его к текущему пользователю.

Требования:
- требуется JWT

### Обновить access token по refresh token

 - [POST /api/auth/refresh-token](https://docs.felmee.com/openapi/autentifikaciya/paths/~1api~1auth~1refresh-token/post.md): Обновляет пару токенов JWT и RT по refresh token.

Refresh token можно передать одним из способов:
- заголовок: RT: Bearer 
- cookie: RT=

### Завершить текущую сессию

 - [POST /api/auth/logout](https://docs.felmee.com/openapi/autentifikaciya/paths/~1api~1auth~1logout/post.md): Завершает текущую сессию пользователя по refresh token.

Требования:
- требуется JWT
- требуется refresh token

Токены можно передать так:
- JWT: заголовок Authorization: Bearer  или cookie JWT=
- RT: заголовок RT: Bearer  или cookie RT=

Важно:
- при успешном вызове этого эндпоинта сервер деактивирует только refresh token
- JWT этим эндпоинтом не деактивируется и остаётся валидным до собственного истечения
- поэтому после успешного logout клиент всё равно обязан удалить и JWT, и RT из своего хранилища

## Файлы

Загрузка файлов и получение доступа к ним.

### Загрузить файл

 - [POST /api/file/upload](https://docs.felmee.com/openapi/fajly/paths/~1api~1file~1upload/post.md): Загружает файл в хранилище.

JSON-part metadata определяет назначение файла и связанные ограничения по типу,
размеру и crop.

Ограничения входных данных:
- файл обязателен и не может быть пустым
- исходное имя файла должно быть непустым и не длиннее 255 символов
- размер файла должен быть больше 0
- для файлов с content-type, начинающимся на image/, максимальный размер составляет 20 MB
- для остальных файлов максимальный размер составляет 100 MB
- для AVATAR разрешены только image/png, image/jpeg, image/jpg, image/webp
- для CARD разрешены только image/png, image/jpeg, image/jpg, image/webp
- для BLOG разрешены только image/png, image/jpeg, image/jpg, image/webp
- для CATEGORY разрешены только image/png, image/jpeg, image/jpg, image/webp
- для AVATAR crop обязателен, должен быть квадратным и не меньше 300x300
- для CARD изображение должно быть квадратным и не меньше 1200x1200; crop передавать нельзя
- для BLOG изображение должно быть квадратным и не меньше 1000x1000; crop передавать нельзя
- для CATEGORY изображение должно быть квадратным и не меньше 300x300; crop передавать нельзя
- для MESSAGE и VOICE crop передавать нельзя
- изображения CARD после загрузки стандартизируются в image/webp и вписываются в 1200x1200
- изображения BLOG после загрузки стандартизируются в image/webp и вписываются в 1000x1000
- изображения CATEGORY после загрузки стандартизируются в image/webp и вписываются в 300x300
- preview создаётся только для purpose, где на backend явно задан размер preview
- сейчас preview создаётся для AVATAR в image/webp, вписанный в 150x150
- сейчас preview создаётся для CARD в image/webp, вписанный в 200x200
- сейчас preview создаётся для MESSAGE image-файлов в image/webp, вписанный в 900x900
- сейчас preview создаётся для BLOG в image/webp, вписанный в 333x333

CARD используется для карточек мастера, сервиса и салона.
BLOG используется для изображений постов.
CATEGORY используется для изображений категорий.

### Скачать файл

 - [GET /api/file/download/{id}](https://docs.felmee.com/openapi/fajly/paths/~1api~1file~1download~1%7Bid%7D/get.md): Возвращает redirect на файл.

Query-параметр type выбирает, какую ссылку вернуть:
- FULL — полный файл
- PREVIEW — preview файла, если он включён для purpose

### Получить статус файла и временную ссылку по id

 - [GET /api/file/{id}](https://docs.felmee.com/openapi/fajly/paths/~1api~1file~1%7Bid%7D/get.md): Возвращает текущее состояние файла и, если файл уже загружен в object storage,
временную presigned URL.

Query-параметр type выбирает, какую ссылку вернуть:
- FULL — полный файл
- PREVIEW — preview файла, если он включён для purpose

Значения data.status:
- PENDING — файл ещё обрабатывается, url = null
- FAILED — обработка завершилась ошибкой, url = null
- UPLOADED — файл доступен, url содержит временную ссылку

Требования:
- требуется JWT

## Профиль

Получение и редактирование профиля текущего пользователя и мастера.

### Получить профиль текущего пользователя

 - [GET /api/user](https://docs.felmee.com/openapi/profil/paths/~1api~1user/get.md): Возвращает профиль текущего авторизованного пользователя.

Имена полей в JSON-ответе возвращаются в формате first_second.

Требования:
- требуется JWT

### Обновить профиль текущего пользователя

 - [PATCH /api/user](https://docs.felmee.com/openapi/profil/paths/~1api~1user/patch.md): Обновляет профиль текущего авторизованного пользователя.

Можно передать любое из полей:
- first_name
- last_name
- city
- avatar
- geolocation
- category_ids

Особенности валидации:
- хотя бы одно из полей должно быть заполнено корректно
- geolocation валиден только если переданы оба поля: lat и lon
- для avatar проверяется доступ пользователя к файлу и возможность использовать файл как аватар
- category_ids, если передан, должен быть массивом существующих ID категорий; пустой массив допустим
- category_ids у пользователя используется для рекомендаций

Требования:
- требуется JWT

### Удалить аккаунт текущего пользователя

 - [DELETE /api/user](https://docs.felmee.com/openapi/profil/paths/~1api~1user/delete.md): Удаляет аккаунт текущего авторизованного пользователя.

После удаления связанные данные пользователя очищаются обработчиками события удаления.

Требования:
- требуется JWT

### Получить профиль текущего мастера

 - [GET /api/master](https://docs.felmee.com/openapi/profil/paths/~1api~1master/get.md): Возвращает профиль текущего авторизованного мастера.

В ответ включаются:
- поля профиля мастера
- пользовательские поля phone, email, first_name, last_name, city, avatar, geolocation, category_ids
- поле min_booking_lead_time_minutes с минимальным временем до записи в минутах
- поле break_after_service_min с длительностью перерыва после услуги в минутах

Значение min_booking_lead_time_minutes изменяется отдельным эндпоинтом POST /api/calendar/min-booking-lead-time.
Значение break_after_service_min изменяется отдельным эндпоинтом POST /api/calendar/break-after-reccord.

Требования:
- требуется JWT

### Обновить профиль текущего мастера

 - [PATCH /api/master](https://docs.felmee.com/openapi/profil/paths/~1api~1master/patch.md): Частично обновляет профиль текущего мастера.

Можно передавать как пользовательские, так и мастерские поля.

Правила валидации:
- валидным считается запрос, в котором корректно заполнено хотя бы одно поле
- first_name, last_name, если переданы, не должны быть пустыми и не должны быть длиннее 100 символов
- city, если передан, не должен быть пустым и не должен быть длиннее 30 символов
- avatar, если передан, должен быть положительным ID файла
- avatar должен принадлежать текущему пользователю и быть допустимым для установки как аватар
- geolocation, если передана, должна содержать lat в диапазоне [-90, 90] и lon в диапазоне [-180, 180]
- category_ids, если передан, должен быть массивом существующих ID категорий; пустой массив допустим
- category_ids у мастера описывает список оказываемых им услуг
- example_work_images, если передан, должен содержать от 1 до 10 положительных ID файлов
- все файлы из example_work_images должны принадлежать текущему пользователю и иметь purpose CARD
- years_experience, если передан, должен быть в диапазоне от 0 до 60
- address, если передан, не должен быть пустым и не должен быть длиннее 1000 символов
- если address передан, backend должен успешно получить geolocation; при неуспешном геокодировании возвращается 422 geocoding_failed
- description, если передан, не должен быть пустым и не должен быть длиннее 1000 символов
- interior_description, если передан, не должен быть пустым и не должен быть длиннее 1000 символов
- interior_images, если передан, должен содержать от 1 до 10 положительных ID файлов
- все файлы из interior_images должны принадлежать текущему пользователю и иметь purpose CARD
- advantages, если передан, должен содержать от 1 до 5 непустых строк длиной до 50 символов каждая
- service_location, если передан, должен быть одним из значений enum

Требования:
- требуется JWT

### Удалить аккаунт текущего мастера

 - [DELETE /api/master](https://docs.felmee.com/openapi/profil/paths/~1api~1master/delete.md): Удаляет аккаунт текущего авторизованного мастера.

Эндпоинт удаляет пользовательский аккаунт, к которому привязан профиль мастера.
Связанные данные мастера и пользователя очищаются каскадно и обработчиками события удаления.

Требования:
- требуется JWT

### Получить подсказки преимуществ мастера

 - [GET /api/master/advantages/suggestions](https://docs.felmee.com/openapi/profil/paths/~1api~1master~1advantages~1suggestions/get.md): Возвращает список строк с подсказками преимуществ для заполнения профиля мастера.

Правила поиска:
- query необязателен
- query обрезается по краям
- если query пустой или не передан, возвращаются первые преимущества по title ASC
- если query передан, возвращаются преимущества, где title содержит query
- поиск регистронезависимый
- limit необязателен, значение по умолчанию 5
- limit безопасно ограничивается диапазоном от 1 до 20

Требования:
- требуется JWT

### Получить публичный профиль мастера по id

 - [GET /api/master/{id}](https://docs.felmee.com/openapi/profil/paths/~1api~1master~1%7Bid%7D/get.md): Возвращает публичный профиль мастера по переданному идентификатору.

Требования:
- требуется JWT

## Календарь

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

### Получить рабочее время мастера

 - [GET /api/calendar/master/{id}/work-time](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1master~1%7Bid%7D~1work-time/get.md): Возвращает рабочее время мастера на указанную дату.

Если на дату нет рабочего интервала, is_working будет false, а start_time и end_time будут null.

Требования:
- требуется JWT
- обязателен path-параметр id
- обязателен query-параметр date в формате YYYY-MM-DD
- нельзя получать рабочее время за прошедшие даты

### Получить календарь текущего пользователя на указанную дату

 - [GET /api/calendar/{date}](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1%7Bdate%7D/get.md): Возвращает календарь текущего авторизованного пользователя для конкретной даты.

Требования:
- требуется JWT
- обязателен path-параметр date в формате YYYY-MM-DD
- нельзя получать календарь за прошедшие даты

### Установить календарь текущего пользователя на указанную дату

 - [POST /api/calendar/{date}](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1%7Bdate%7D/post.md): Устанавливает временной интервал текущего авторизованного пользователя на указанную дату.

Если запись на эту дату уже существует, она полностью перезаписывается.
Если записи ещё нет, она создаётся.
Если существующие записи BOOKED выходят за пределы нового интервала, возвращается конфликт 409.

Правила валидации:
- date обязателен и должен быть не раньше текущего дня
- time_interval может быть null, чтобы очистить календарь на дату
- если time_interval передан, внутри интервала start_time и end_time не должны быть null
- end_time должен быть позже start_time в рамках одного дня
- минимальная длительность интервала: 30 минут

Доступ:
- только роли MASTER или SALON
- требуется JWT

### Установить рабочий график текущего пользователя

 - [POST /api/calendar/work-schedule](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1work-schedule/post.md): Устанавливает рабочее время и перерывы на диапазон дней выбранного месяца.

Перед сохранением проверяются конфликты с существующими записями. Если конфликтов нет, график применяется к дням диапазона и сохраняется.

Доступ:
- только роли MASTER или SALON
- требуется JWT

### Получить рабочий график

 - [GET /api/calendar/work-schedule](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1work-schedule/get.md): Возвращает рабочий график по id или список графиков по user_id и year.

Требования:
- требуется JWT
- передайте либо id, либо пару user_id + year

### Получить минимальное время до записи текущего мастера

 - [GET /api/calendar/min-booking-lead-time](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1min-booking-lead-time/get.md): Возвращает значение min_booking_lead_time_minutes текущего авторизованного мастера.

Это значение определяет, за сколько минут минимум можно создать новую запись.

Требования:
- требуется JWT
- доступно только роли MASTER

### Установить минимальное время до записи текущего мастера

 - [POST /api/calendar/min-booking-lead-time](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1min-booking-lead-time/post.md): Устанавливает значение min_booking_lead_time_minutes для текущего авторизованного мастера.

Правила валидации:
- min_booking_lead_time_minutes обязателен
- значение должно быть JSON integer в диапазоне от 0 до 360 минут

Требования:
- требуется JWT
- доступно только роли MASTER

### Получить перерыв после услуги текущего мастера

 - [GET /api/calendar/break-after-reccord](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1break-after-reccord/get.md): Возвращает значение break_after_service_min текущего авторизованного мастера.

Это значение используется как перерыв после услуги при расчёте доступных слотов записи.

Требования:
- требуется JWT
- доступно только роли MASTER

### Установить перерыв после услуги текущего мастера

 - [POST /api/calendar/break-after-reccord](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1break-after-reccord/post.md): Устанавливает значение break_after_service_min для текущего авторизованного мастера.

Правила валидации:
- break_after_service_min обязателен
- значение должно быть JSON integer в диапазоне от 0 до 300 минут

Требования:
- требуется JWT
- доступно только роли MASTER

### Установить перерывы текущего пользователя на указанную дату

 - [POST /api/calendar/break](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1break/post.md): Сохраняет список пользовательских перерывов на указанную дату.

Перед сохранением интервалы сортируются по start_time, а затем полностью перезаписывают уже сохранённые перерывы на эту дату.
Если передан пустой список intervals, перерывы на дату очищаются.
Если хотя бы одна существующая запись со статусом BOOKED пересекается с любым интервалом из нового списка, возвращается конфликт 409.

Правила валидации:
- date обязателен и не должен быть раньше текущего дня
- intervals обязателен, но может быть пустым
- каждый элемент intervals должен быть не null
- внутри каждого интервала start_time и end_time обязательны
- end_time должен быть позже start_time в рамках одного дня
- минимальная длительность одного перерыва: 15 минут
- интервалы перерывов не должны пересекаться между собой

Доступ:
- только роли MASTER или SALON
- требуется JWT

### Получить перерывы текущего пользователя на указанную дату

 - [GET /api/calendar/break/{date}](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1break~1%7Bdate%7D/get.md): Возвращает список перерывов текущего пользователя на указанную дату.

Если перерывы на дату не заданы, возвращается пустой список intervals.

Требования:
- требуется JWT
- обязательный path-параметр date в формате YYYY-MM-DD
- нельзя получать перерывы за прошедшие даты

### Установить акции текущего пользователя на указанную дату

 - [PATCH /api/calendar/action](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1action/patch.md): Сохраняет список акционных интервалов на указанную дату.

Перед сохранением список полностью перезаписывает уже сохранённые акции на дату.

Правила валидации:
- date обязателен и не должен быть раньше текущего дня
- actions обязателен
- каждый элемент actions должен быть не null
- у каждого action поле percent_action обязательно и должно быть в диапазоне от 1 до 99
- у каждого action поле interval обязательно
- внутри каждого интервала start_time и end_time обязательны
- end_time должен быть позже start_time в рамках одного дня
- минимальная длительность одного акционного интервала: 15 минут
- акционные интервалы не должны пересекаться между собой

Требования:
- требуется JWT

### Получить акции текущего пользователя на указанную дату

 - [GET /api/calendar/action/{date}](https://docs.felmee.com/openapi/kalendar/paths/~1api~1calendar~1action~1%7Bdate%7D/get.md): Возвращает список акционных интервалов текущего пользователя на указанную дату.

Если акции на дату не заданы, возвращается пустой список actions.

Требования:
- требуется JWT
- обязательный path-параметр date в формате YYYY-MM-DD
- нельзя получать акции за прошедшие даты

## Услуги

Создание, редактирование, удаление и получение услуг.

### Создать услугу

 - [POST /api/service](https://docs.felmee.com/openapi/uslugi/paths/~1api~1service/post.md): Создает новую услугу текущего пользователя.

Правила валидации:
- name не должен быть пустым и не должен быть длиннее 120 символов
- description не должен быть пустым и не должен быть длиннее 4000 символов
- price должен быть в диапазоне от 1 до 100000
- duration должен быть в диапазоне от 1 до 1440 минут
- category_id должен быть положительным ID существующей категории
- image должен быть положительным ID файла
- work_example_images должен содержать от 1 до 10 положительных ID файлов
- service_location должен быть передан
- address обязателен для всех service_location, кроме CLIENT_PLACE; если передан, не должен быть длиннее 1000 символов
- advantages должен содержать не больше 5 уникальных непустых строк до 50 символов

Доступ:
- только роли MASTER или SALON
- файлы из image и work_example_images должны быть доступны текущему пользователю
- файлы из image и work_example_images должны иметь purpose CARD
- если address передан, backend должен успешно получить geo; при неуспешном геокодировании возвращается 422 geocoding_failed
- требуется JWT

### Получить свои услуги мастера

 - [GET /api/service/master](https://docs.felmee.com/openapi/uslugi/paths/~1api~1service~1master/get.md): Возвращает список услуг текущего авторизованного мастера.

Если у мастера нет услуг, сервер возвращает пустой массив.

Доступ:
- только роль MASTER
- требуется JWT

### Получить услугу по id

 - [GET /api/service/{serviceId}](https://docs.felmee.com/openapi/uslugi/paths/~1api~1service~1%7Bserviceid%7D/get.md): Возвращает услугу по ее идентификатору.

### Обновить услугу

 - [PATCH /api/service/{serviceId}](https://docs.felmee.com/openapi/uslugi/paths/~1api~1service~1%7Bserviceid%7D/patch.md): Частично обновляет услугу по ее идентификатору.

Правила валидации:
- хотя бы одно поле должно быть заполнено корректно
- name, если передан, не должен быть пустым и не должен быть длиннее 120 символов
- price должен быть в диапазоне от 1 до 100000
- duration должен быть в диапазоне от 1 до 1440 минут
- description, если передан, не должен быть пустым и не должен быть длиннее 4000 символов
- category_id, если передан, должен быть положительным ID существующей категории
- если передан image, он должен быть положительным ID файла
- если передан work_example_images, он должен содержать от 1 до 10 положительных ID файла
- если переданы image или work_example_images, все файлы должны иметь purpose CARD
- если передан address, backend должен успешно получить geo; при неуспешном геокодировании возвращается 422 geocoding_failed

Доступ:
- требуется JWT
- пользователь должен иметь доступ к услуге
- если переданы image или work_example_images, все файлы должны быть доступны пользователю

### Удалить услугу

 - [DELETE /api/service/{serviceId}](https://docs.felmee.com/openapi/uslugi/paths/~1api~1service~1%7Bserviceid%7D/delete.md): Удаляет услугу по ее идентификатору.

Требования:
- требуется JWT
- пользователь должен иметь доступ к услуге
- услугу нельзя удалить, если по ней уже есть бронирования

### Получить услуги мастера или салона

 - [GET /api/service/master/{masterId}](https://docs.felmee.com/openapi/uslugi/paths/~1api~1service~1master~1%7Bmasterid%7D/get.md): Возвращает список услуг, принадлежащих указанному мастеру или салону.

Если у пользователя нет услуг, сервер возвращает пустой массив.

## Поиск

Поиск услуг с фильтрацией, сортировкой и геолокацией.

### Поиск услуг

 - [GET /api/search/services](https://docs.felmee.com/openapi/poisk/paths/~1api~1search~1services/get.md): Выполняет поиск услуг с поддержкой текстового поиска,
фильтрации по категории, цене и геолокации, а также пагинации и сортировки.

Особенности:
- q необязателен
- должен быть передан хотя бы один параметр поиска или фильтрации
- category_id фильтрует по выбранной категории
- lat и lon должны передаваться вместе
- radius_km можно использовать только вместе с lat и lon
- сортировки DISTANCE_ASC и DISTANCE_DESC требуют lat и lon
- price_to должен быть больше или равен price_from
- требуется JWT
- при превышении лимита поиска возвращается 429 too_many_request с data.TTL

### Поиск мастеров

 - [GET /api/search/masters](https://docs.felmee.com/openapi/poisk/paths/~1api~1search~1masters/get.md): Выполняет поиск мастеров с поддержкой текстового поиска,
фильтрации по категории, диапазону цен и геолокации, а также пагинации и сортировки.

Особенности:
- q необязателен
- должен быть передан хотя бы один параметр поиска или фильтрации
- category_id фильтрует мастеров по выбранной категории
- lat и lon должны передаваться вместе
- radius_km можно использовать только вместе с lat и lon
- сортировки DISTANCE_ASC и DISTANCE_DESC требуют lat и lon
- price_to должен быть больше или равен price_from
- требуется JWT
- при превышении лимита поиска возвращается 429 too_many_request с data.TTL

## Категории

Получение и управление категориями услуг.

### Получить корневые категории

 - [GET /api/category](https://docs.felmee.com/openapi/kategorii/paths/~1api~1category/get.md): Возвращает список корневых категорий (parent_id = null).

Каждая категория может содержать вложенный список children.

Требования:
- требуется JWT

### Создать категорию

 - [POST /api/category](https://docs.felmee.com/openapi/kategorii/paths/~1api~1category/post.md): Создаёт новую категорию.

Правила валидации:
- name обязателен, не должен быть пустым и не должен быть длиннее 100 символов
- parent_id, если передан, должен быть положительным
- parent_id, если передан, должен ссылаться на существующую категорию
- image, если передан, должен быть положительным ID файла с purpose CARD

Доступ:
- только ADMIN
- требуется JWT

### Получить категорию по id

 - [GET /api/category/{categoryId}](https://docs.felmee.com/openapi/kategorii/paths/~1api~1category~1%7Bcategoryid%7D/get.md): Возвращает категорию по идентификатору вместе со списком children.

Требования:
- требуется JWT

### Обновить категорию

 - [PATCH /api/category/{categoryId}](https://docs.felmee.com/openapi/kategorii/paths/~1api~1category~1%7Bcategoryid%7D/patch.md): Частично обновляет категорию по идентификатору.

Правила валидации:
- хотя бы одно поле должно быть заполнено корректно
- name, если передан, не должен быть пустым и не должен быть длиннее 100 символов
- parent_id, если передан, должен быть положительным
- parent_id не может совпадать с categoryId
- цепочка родителей не должна содержать саму категорию
- image, если передан, должен быть положительным ID файла с purpose CARD

Доступ:
- только ADMIN
- требуется JWT

### Удалить категорию

 - [DELETE /api/category/{categoryId}](https://docs.felmee.com/openapi/kategorii/paths/~1api~1category~1%7Bcategoryid%7D/delete.md): Удаляет категорию по идентификатору.

Ограничения:
- категория не должна иметь дочерних категорий
- категория не должна использоваться в service

Доступ:
- только ADMIN
- требуется JWT

### Получить onboarding категории

 - [GET /api/category/onboarding](https://docs.felmee.com/openapi/kategorii/paths/~1api~1category~1onboarding/get.md): Возвращает список категорий, у которых showInOnboarding = true.

Если к категории привязано изображение, в ответе будет поле image_id.

Требования:
- требуется JWT

### Получить дочерние категории

 - [GET /api/category/{categoryId}/children](https://docs.felmee.com/openapi/kategorii/paths/~1api~1category~1%7Bcategoryid%7D~1children/get.md): Возвращает непосредственных дочерних категорий для указанной категории.

Требования:
- требуется JWT

## Запись

Бронирование услуг и получение доступных слотов.

### Получить аналитику мастера за дату

 - [GET /api/master/analytics/{date}](https://docs.felmee.com/openapi/zapis/paths/~1api~1master~1analytics~1%7Bdate%7D/get.md): Требования:
- требуется JWT
- текущий пользователь должен быть мастером

### Создать запись

 - [POST /api/record/](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1/post.md): Создаёт запись на услугу.

Перед сохранением сервер проверяет:
- корректность тела запроса
- что текущий пользователь имеет роль USER
- что услуга существует
- что переданный интервал точно совпадает хотя бы с одним доступным слотом
- что start раньше end
- что длительность интервала не превышает 24 часа

Требования:
- требуется JWT

### Получить запись по id

 - [GET /api/record/{id}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1%7Bid%7D/get.md): Возвращает запись по идентификатору, если текущий пользователь имеет к ней доступ.

Требования:
- требуется JWT
- только роль USER

### Отменить запись

 - [POST /api/record/{id}/cancel](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1%7Bid%7D~1cancel/post.md): Отменяет существующую запись без физического удаления.
Сервер переводит запись в статус CANCELED.

Перед отменой сервер проверяет:
- корректность тела запроса
- что id является положительным числом
- что запись с таким id существует
- что текущий пользователь имеет доступ к этой записи
- что пользователь с ролью USER отменяет запись не менее чем за 5 часов до её начала

Правила доступа:
- роль USER может отменить только свою запись
- роль MASTER может отменить только запись, принадлежащую этому мастеру

Требования:
- требуется JWT

### Создать запрос на перенос записи

 - [POST /api/record/reschedule](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1reschedule/post.md): Создаёт запрос на перенос существующей записи.

Перед созданием сервер проверяет:
- корректность тела запроса
- что запись с таким record_id существует
- что текущий пользователь является участником этой записи
- что запись находится в статусе BOOKED
- что до начала записи осталось не менее 1 часа
- что для пары record_id + sender_id еще не существует запроса на перенос

После создания в Messenger отправляется request-сообщение:
- action = RESCHEDULE
- source_request_id = reschedule_record_request.id
- payload соответствует RescheduleRequestPayload

Требования:
- требуется JWT

### Получить запросы на перенос записи по дате

 - [GET /api/record/reschedule/{date}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1reschedule~1%7Bdate%7D/get.md): Возвращает список запросов на перенос записи на указанную дату.

В выборку попадают запросы, связанные с записями, стартующими в эту дату
в часовом поясе сервиса.

Требования:
- требуется JWT
- date не должна быть в прошлом

### Отклонить запрос на перенос записи

 - [POST /api/record/reschedule/reject/{id}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1reschedule~1reject~1%7Bid%7D/post.md): Отклоняет ранее созданный запрос на перенос записи.

Перед отклонением сервер проверяет:
- корректность id
- что запрос существует
- что его статус равен WAITING_APPROVE
- что текущий пользователь имеет право отклонить этот запрос

После reject Messenger обновляет статус request-сообщения и отправляет
service message RESULT_RESCHEDULE_RECORD.

Требования:
- требуется JWT

### Подтвердить запрос на перенос записи

 - [POST /api/record/reschedule/approve/{id}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1reschedule~1approve~1%7Bid%7D/post.md): Подтверждает ранее созданный запрос на перенос записи и задает новое время записи.

Перед подтверждением сервер проверяет:
- корректность id
- корректность new_start и new_end
- что запрос существует
- что его статус равен WAITING_APPROVE
- что запрос еще не истек
- что текущий пользователь имеет право подтвердить этот запрос
- что исходная запись еще не началась (now  new_end доступен для услуги записи

Если исходная запись уже началась, подтверждение блокируется и статусы
запроса в service_record и Messenger остаются WAITING_APPROVE.

После approve Messenger обновляет статус request-сообщения и отправляет
service message RESULT_RESCHEDULE_RECORD`.

Требования:
- требуется JWT

### Создать запрос на создание записи

 - [POST /api/record/create](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1create/post.md): Создаёт запрос на создание записи по услуге для выбранного пользователя.

Перед созданием сервер проверяет:
- корректность тела запроса
- что пользователь user_id существует и имеет роль USER
- что услуга service_id существует
- что текущий пользователь является владельцем/мастером этой услуги

Время записи в этом запросе не передаётся. Слот выбирается позднее,
при подтверждении запроса через /api/record/create/approve/{id}.

После создания в Messenger отправляется request-сообщение:
- action = CREATE_RECORD
- source_request_id = create_record_request.id
- payload соответствует CreateRecordRequestPayload

Требования:
- требуется JWT
- endpoint доступен мастеру-владельцу услуги

### Получить запросы на создание записи по дате создания

 - [GET /api/record/create/{date}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1create~1%7Bdate%7D/get.md): Возвращает список запросов на создание записи, созданных в указанную дату.

Важно: фильтрация выполняется по at_create / time_create, а не по
будущему времени услуги. Время услуги появляется только после approve.

Требования:
- требуется JWT
- date не должна быть в прошлом

### Отклонить запрос на создание записи

 - [POST /api/record/create/reject/{id}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1create~1reject~1%7Bid%7D/post.md): Отклоняет ранее созданный запрос на создание записи.

Перед отклонением сервер проверяет:
- корректность id
- что текущий пользователь имеет роль USER
- что запрос существует
- что его статус равен WAITING_APPROVE
- что текущий пользователь является получателем этого запроса

После reject Messenger обновляет статус request-сообщения.
Service message для CREATE_RECORD не создаётся.

Требования:
- требуется JWT

### Подтвердить запрос на создание записи

 - [POST /api/record/create/approve/{id}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1create~1approve~1%7Bid%7D/post.md): Подтверждает ранее созданный запрос на создание записи и создаёт service record
на выбранный интервал времени.

Перед подтверждением сервер проверяет:
- корректность тела запроса
- что текущий пользователь существует и имеет роль USER
- что запрос существует
- что его статус равен WAITING_APPROVE
- что текущий пользователь является получателем этого запроса
- что услуга запроса существует
- что start находится в будущем
- что длительность не превышает максимально разрешённую длительность записи
- что соблюдён min_booking_lead_time мастера
- что слот start -> end доступен для услуги

После approve Messenger обновляет статус request-сообщения.
Service message для CREATE_RECORD не создаётся.

Требования:
- требуется JWT

### Создать запрос на доплату

 - [POST /api/record/surcharge](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1surcharge/post.md): Создаёт request на дополнительную оплату по услуге.

Перед созданием сервер проверяет:
- корректность тела запроса
- что услуга service_id существует
- что текущий пользователь является владельцем/мастером этой услуги

После создания в Messenger отправляется request-сообщение:
- action = SURCHARGE
- source_request_id = surcharge_record_request.id
- кнопки PAY / REJECT
- payload соответствует SurchargeRequestPayload

Требования:
- требуется JWT
- endpoint доступен мастеру-владельцу услуги

### Получить запросы на доплату по дате создания

 - [GET /api/record/surcharge/{date}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1surcharge~1%7Bdate%7D/get.md): Возвращает список запросов на доплату, созданных в указанную дату.

Фильтрация выполняется по at_create / time_create в часовом поясе сервиса.

Требования:
- требуется JWT
- date не должна быть в прошлом

### Отклонить запрос на доплату

 - [POST /api/record/surcharge/reject/{id}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1surcharge~1reject~1%7Bid%7D/post.md): Отклоняет ранее созданный запрос на доплату и сохраняет причину отказа.

Перед отклонением сервер проверяет:
- корректность id и body запроса
- что текущий пользователь имеет роль USER
- что запрос существует
- что его статус равен WAITING_APPROVE
- что текущий пользователь является получателем этого запроса

После reject Messenger обновляет статус request-сообщения и отправляет
service message RESULT_SURCHARGE_REQUEST с status = REJECTED и reject_reason.

Требования:
- требуется JWT

### Тестово опубликовать событие успешной оплаты доплаты

 - [POST /api/record/surcharge/pay/{request_id}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1surcharge~1pay~1%7Brequest_id%7D/post.md): Тестовый endpoint для проверки flow оплаты запроса на доплату.

Публикует SurchargePaymentSucceededEvent с указанным request_id.
Listener service_record обрабатывает событие, переводит surcharge_record_request
в APPROVED и отправляет событие approve в Messenger.

После обработки Messenger обновляет статус request-сообщения и отправляет
service message RESULT_SURCHARGE_REQUEST с status = APPROVED и reject_reason = null.

Endpoint временный и может быть удалён.

Требования:
- требуется JWT

### Тестово отметить запись как оплаченную

 - [POST /api/record/pay/{id}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1pay~1%7Bid%7D/post.md): Тестовый endpoint для фронтенд-проверки flow оплаты.

Напрямую обновляет service_records.status в BOOKED для записи с указанным id.
Бизнес-валидации, проверки сервиса, владельца, текущего статуса и оплаты не выполняются.
Endpoint временный и может быть удалён.

Требования:
- требуется JWT

### Получить доступные слоты для бронирования услуги

 - [GET /api/record/free-slots](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1free-slots/get.md): Возвращает список доступных слотов для выбранной услуги на указанную дату.

Слот формируется на основе:
- рабочего календаря мастера
- уже занятых интервалов
- длительности услуги

Ограничения входных данных:
- service_id должен быть положительным
- service_id должен ссылаться на существующую услугу
- date должен быть сегодня или в будущем

Требования:
- требуется JWT

### Получить записи текущего пользователя

 - [GET /api/record/user](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1user/get.md): Возвращает список записей текущего пользователя.

Требования:
- требуется JWT
- только роль USER
- текущий пользователь должен иметь доступ на получение записей

### Получить записи текущего мастера

 - [GET /api/record/master](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1master/get.md): Возвращает список записей текущего мастера.

Требования:
- требуется JWT
- только роль MASTER

### Получить записи текущего мастера по дате

 - [GET /api/record/master/{date}](https://docs.felmee.com/openapi/zapis/paths/~1api~1record~1master~1%7Bdate%7D/get.md): Требования:
- требуется JWT
- только роль MASTER
- обязательный path-параметр date в формате YYYY-MM-DD

## Отзовы

Создание и получение отзывов о завершённых записях.

### Создать отзыв по завершённой записи

 - [POST /api/review](https://docs.felmee.com/openapi/otzovy/paths/~1api~1review/post.md): Создаёт новый отзыв текущего пользователя по service record.

Требования:
- требуется JWT
- отзыв можно оставить только на собственную завершённую запись
- на одну запись можно оставить только один отзыв
- все media_ids, если переданы, должны быть доступны пользователю и иметь purpose CARD

### Получить отзывы текущего пользователя

 - [GET /api/review/user](https://docs.felmee.com/openapi/otzovy/paths/~1api~1review~1user/get.md): Возвращает все отзывы, оставленные текущим авторизованным пользователем.

Требования:
- требуется JWT

### Получить отзывы по мастеру

 - [GET /api/review/master/{masterId}](https://docs.felmee.com/openapi/otzovy/paths/~1api~1review~1master~1%7Bmasterid%7D/get.md): Возвращает все отзывы, привязанные к указанному мастеру.

### Получить отзывы по услуге

 - [GET /api/review/service/{service_id}](https://docs.felmee.com/openapi/otzovy/paths/~1api~1review~1service~1%7Bservice_id%7D/get.md): Возвращает все отзывы, оставленные по записям указанной услуги.

Отзывы выбираются через связь reviews.service_record_id -> service_records.id
и фильтруются по service_records.service_id.

### Получить отзывы по записи

 - [GET /api/review/record/{id}](https://docs.felmee.com/openapi/otzovy/paths/~1api~1review~1record~1%7Bid%7D/get.md): Возвращает все отзывы, оставленные по указанной записи.

### Получить отзыв по идентификатору

 - [GET /api/review/{id}](https://docs.felmee.com/openapi/otzovy/paths/~1api~1review~1%7Bid%7D/get.md): Возвращает один отзыв по его идентификатору.

Возможные ошибки:
- 404 not_found - отзыв не найден

## Блог

Создание, редактирование и получение постов.

### Получить свои посты

 - [GET /api/blog](https://docs.felmee.com/openapi/blog/paths/~1api~1blog/get.md): Возвращает список постов текущего авторизованного пользователя.

Требования:
- требуется JWT

### Создать пост

 - [POST /api/blog](https://docs.felmee.com/openapi/blog/paths/~1api~1blog/post.md): Создаёт новый пост текущего пользователя.

Правила валидации:
- text обязателен, не пустой, максимум 30 символов
- title обязателен, не пустой, максимум 100 символов
- images должен содержать от 1 до 10 положительных ID файлов
- categories должен содержать от 1 до 10 положительных ID категорий

Доступ:
- требуется JWT
- требуется роль MASTER
- все файлы из images должны быть доступны текущему пользователю и иметь purpose BLOG

### Получить пост по id

 - [GET /api/blog/{id}](https://docs.felmee.com/openapi/blog/paths/~1api~1blog~1%7Bid%7D/get.md): Возвращает один пост по его идентификатору.

### Обновить пост

 - [PATCH /api/blog/{id}](https://docs.felmee.com/openapi/blog/paths/~1api~1blog~1%7Bid%7D/patch.md): Частично обновляет пост по его идентификатору.

Правила валидации:
- хотя бы одно поле должно быть заполнено корректно
- text, если передан, не должен быть пустым и не должен быть длиннее 30 символов
- title, если передан, не должен быть пустым и не должен быть длиннее 100 символов
- images, если передан, должен содержать от 1 до 10 уникальных положительных ID файлов
- categories, если передан, должен содержать от 1 до 10 уникальных положительных ID категорий

Доступ:
- требуется JWT
- требуется роль MASTER
- пользователь должен иметь доступ к посту
- если передан images, все файлы должны быть доступны текущему пользователю и иметь purpose BLOG

### Получить посты пользователя

 - [GET /api/blog/master/{userId}](https://docs.felmee.com/openapi/blog/paths/~1api~1blog~1master~1%7Buserid%7D/get.md): Возвращает список постов указанного пользователя.

## Чаты REST

REST-эндпоинты модуля мессенджера для получения диалогов и сообщений.

### Получить диалог по id

 - [GET /api/dialogs/{id}](https://docs.felmee.com/openapi/chaty-rest/paths/~1api~1dialogs~1%7Bid%7D/get.md): Возвращает диалог по идентификатору, если:
- диалог существует
- текущий пользователь имеет к нему доступ

Требования:
- требуется JWT

### Получить список диалогов текущего пользователя

 - [GET /api/dialogs/user](https://docs.felmee.com/openapi/chaty-rest/paths/~1api~1dialogs~1user/get.md): Возвращает все диалоги, участником которых является текущий пользователь.

Требования:
- требуется JWT

### Отправить сообщение

 - [POST /api/messages/send](https://docs.felmee.com/openapi/chaty-rest/paths/~1api~1messages~1send/post.md): Создаёт одно или несколько сообщений и возвращает список их идентификаторов.

Поддерживаются два сценария:
- отправка в существующий диалог: передайте dialog_id
- отправка напрямую пользователю: передайте receiver_id

Обязательные условия:
- должен быть указан хотя бы один из параметров dialog_id или receiver_id
- должно быть заполнено хотя бы одно из полей text или files
- text, если передан, не должен быть длиннее 4000 символов
- files, если передан, должен содержать не более 10 положительных ID файлов
- dialog_id и receiver_id, если переданы, должны быть положительными
- требуется JWT

Возможные ошибки:
- 400 invalid_body - запрос не прошёл валидацию или бизнес-проверки
- 403 forbidden - нет прав на отправку сообщения или доступ к файлам
- 404 not_found - не найден диалог или получатель

### Получить сообщения диалога

 - [GET /api/messages](https://docs.felmee.com/openapi/chaty-rest/paths/~1api~1messages/get.md): Возвращает сообщения диалога постранично через offset.

Важно:
backend ожидает параметры id_dialog и offset в query string.

Требования:
- требуется JWT

Возможные ошибки:
- 400 invalid_body - не пройдена Bean Validation (id_dialog равен null или меньше 1, offset равен null, меньше 0 или больше 100000)
- 403 forbidden - нет доступа к диалогу
- 404 not_found - диалог не найден

### Получить сообщение по идентификатору

 - [GET /api/messages/{id}](https://docs.felmee.com/openapi/chaty-rest/paths/~1api~1messages~1%7Bid%7D/get.md): Возвращает одно сообщение по его идентификатору.

Требования:
- требуется JWT

Возможные ошибки:
- 400 invalid_body - id равен null, 0 или меньше 0
- 403 forbidden - нет доступа к диалогу, к которому относится сообщение
- 404 not_found - сообщение не найдено

## Чаты WS

WebSocket/STOMP-эндпоинты модуля мессенджера.

Подключение выполняется через SockJS endpoint `/ws`.
Для вызова STOMP destinations должно уже существовать аутентифицированное
STOMP-соединение.
JWT передаётся один раз на этапе STOMP CONNECT в заголовке
`Authorization: Bearer <JWT>`.


### Отправить сообщение через WebSocket

 - [POST /app/chat.send](https://docs.felmee.com/openapi/chaty-ws/paths/~1app~1chat.send/post.md): STOMP destination для отправки сообщений через WebSocket.

Подключение:
- SockJS endpoint: /ws
- STOMP destination: /app/chat.send

Клиент отправляет тот же payload, что и REST-эндпоинт отправки сообщения.

Ответы:
- в ответ отправителю приходит сообщение в /user/queue/chat.delivered
- собеседнику отправляется уведомление в /user/queue/chat.new_message
- ошибки приходят в /user/queue/errors

### Отметить сообщения прочитанными через WebSocket

 - [POST /app/chat.read](https://docs.felmee.com/openapi/chaty-ws/paths/~1app~1chat.read/post.md): STOMP destination для отметки сообщений как прочитанных.

Подключение:
- SockJS endpoint: /ws
- STOMP destination: /app/chat.read

При успешной обработке другому участнику диалога отправляется уведомление
в /user/queue/chat.read_message.
Ошибки приходят в /user/queue/errors.

### Поставить или убрать реакцию через WebSocket

 - [POST /app/chat.reaction](https://docs.felmee.com/openapi/chaty-ws/paths/~1app~1chat.reaction/post.md): STOMP destination для установки реакции на сообщение.

Подключение:
- SockJS endpoint: /ws
- STOMP destination: /app/chat.reaction

Если reaction_type = null, реакция пользователя удаляется.

При успешной обработке другому участнику диалога отправляется уведомление
в /user/queue/chat.reaction. В событии реакции передаются message_id,
user_id, reaction_type и dialog_id.
Ошибки приходят в /user/queue/errors.

### Подписка на подтверждение доставки отправителю

 - [GET /user/queue/chat.delivered](https://docs.felmee.com/openapi/chaty-ws/paths/~1user~1queue~1chat.delivered/get.md): Пользовательская очередь, в которую сервер отправляет результат успешной
отправки сообщения после вызова /app/chat.send.

### Подписка на новые входящие сообщения

 - [GET /user/queue/chat.new_message](https://docs.felmee.com/openapi/chaty-ws/paths/~1user~1queue~1chat.new_message/get.md): Пользовательская очередь для получения новых сообщений от другого участника
диалога.

В сообщениях может присутствовать дополнительное поле request с телом
интерактивного запроса, например запроса на перенос записи (RESCHEDULE),
запроса на создание записи (CREATE_RECORD) или запроса на доплату (SURCHARGE), а также поле
service_message со служебным сообщением.

### Подписка на обновление статуса request-сообщения

 - [GET /user/queue/chat.update_status_request](https://docs.felmee.com/openapi/chaty-ws/paths/~1user~1queue~1chat.update_status_request/get.md): Пользовательская очередь для получения уведомления об изменении статуса
request внутри сообщения, например когда собеседник подтвердил или отклонил
запрос на перенос записи.

### Подписка на событие прочтения сообщений

 - [GET /user/queue/chat.read_message](https://docs.felmee.com/openapi/chaty-ws/paths/~1user~1queue~1chat.read_message/get.md): Пользовательская очередь для уведомления о прочтении сообщений другим
участником диалога.

### Подписка на реакции в сообщениях

 - [GET /user/queue/chat.reaction](https://docs.felmee.com/openapi/chaty-ws/paths/~1user~1queue~1chat.reaction/get.md): Пользовательская очередь для получения уведомлений о постановке или
удалении реакции другим участником диалога.

### Подписка на ошибки WebSocket-модуля мессенджера

 - [GET /user/queue/errors](https://docs.felmee.com/openapi/chaty-ws/paths/~1user~1queue~1errors/get.md): Пользовательская очередь, в которую сервер отправляет ошибки валидации и
обработки STOMP-сообщений.

