{"openapi":"3.1.0","info":{"title":"Review Aggregator","description":"Единый REST API для отзывов: сервис собирает отзывы с площадок (Яндекс Карты, 2ГИС, Google, Ostrovok, ДокДок и др.), приводит их к общей модели и отдаёт с фильтрами и аналитикой. Под ваши задачи можно добавить новые площадки и функциональность.\n\n📚 **Гайды, quickstart и полный справочник — на портале разработчика: https://docs.rewio.ru**\n\n#### Как начать\n1. Создайте **группу** — ваш объект.\n2. Добавьте в неё **ссылки** на страницы объекта на нужных площадках.\n3. Запрашивайте **отзывы** и **аналитику** — данные обновляются несколько раз в день.\n\n#### Что вы получаете по каждому отзыву\n- **Текст**, **автор**, **оценка** — приведена к единой шкале 1–5 независимо от площадки\n- **Ответ организации** — текст и дата\n- **Изображения и видео** — превью и полноразмерные ссылки\n\n#### Соглашения\n- **Базовый URL:** `https://api.rewio.ru`. Актуальная версия — `/v2`.\n- **Авторизация:** заголовок `X-API-Key: <ключ>` в каждом запросе. Области ключа: `read_only` / `full`.\n- **Формат:** JSON на вход и выход.\n- **Даты:** UTC, ISO 8601 без таймзоны (`2026-02-25T09:28:07`). Исключение — `review_date` (дата отзыва): это местное время площадки, как показано на её сайте.\n- **Оценки:** нормализованы к единой шкале 1–5 независимо от площадки; исходное значение площадки сохраняется отдельно (`rating_original`).\n- **Списки:** ключ элементов всегда `data`. Постраничные ответы — `{data, total, limit, offset, has_next}` (параметры `limit` 1–1000 и `offset`); полные списки — только `{data}`.\n- **Ошибки:** единый формат `{\"detail\": \"...\", \"code\": \"...\"}` с машиночитаемым `code`. Отдельно стоит `402`: доступ приостановлен (закончился пробный период или оплата) либо площадка не входит в тариф — повтор запроса не поможет.\n- **Ограничение частоты:** при слишком частых запросах — `429`; повторите с задержкой.\n\n#### Проба\nВ блоке авторизации уже подставлен тестовый ключ (только чтение) — можно сразу выполнить любой GET-запрос.\n\n**Тестовый ключ (только чтение):** `hrev_FaDGMKJEDDeCR5BCBgEg0aEjMogI7O7hOzE53YY4T1M`  \n**Тестовая группа:** Отель Националь, **group_id = 20**\n\nБесплатный тестовый доступ на создание 10 групп.  \n**Подключение и доступ:** 📧 info@rewio.ru · ✈️ @const_rewio","version":"2.0.0"},"servers":[{"url":"https://api.rewio.ru","description":"Production"}],"paths":{"/health":{"get":{"tags":["health"],"summary":"Проверка доступности","description":"Отвечает, если API на связи. Ключ не нужен.\n\nКлюч метод не проверяет, поэтому отделяет сеть от ключа: молчит — дело в\nсети или на нашей стороне, отвечает, а запрос с ключом даёт `401` — дело\nв ключе.","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/health/read":{"get":{"tags":["health"],"summary":"Проверка чтения данных","description":"Отвечает `200`, если чтение данных работает: методы отзывов и аналитики\nответят. Если нет — `503`. Ключ не нужен.\n\nПодходит для внешнего мониторинга доступности.","operationId":"read_health_check_health_read_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/reviews":{"get":{"tags":["reviews"],"summary":"Получить отзывы группы или папки","description":"Отзывы группы или папки с фильтрацией, сортировкой и пагинацией.\n\nАдресовать нужно **ровно один** объект: либо `group_id` (одна группа), либо\n`folder_id` (папка целиком — все её группы и группы её подпапок). Оба сразу —\n`400 group_and_folder_conflict`, ни одного — `400 group_or_folder_required`.\n\nОтвет — конверт `Page`: `{data, total, limit, offset, has_next}`. По каждому\nотзыву: `author`, `text`, `rating` (шкала 1–5), `reply_text`/`reply_date`,\n`images`, и т.д.\n\nДиапазон дат публикации — `published_from`/`published_to`; фильтр ответа —\n`has_reply`. `link_ids` и `source_ids` фильтруют **внутри** адресованного\nсостава.\n\n**Сортировка:** `sort_by=date` (по умолчанию), `rating` или `reply_date` —\nпо дате ответа организации; `sort_order=desc/asc`. Отзывы без ответа\n(`reply_date` пуст) идут **в конце** при обоих направлениях, поэтому\n`sort_by=reply_date&sort_order=asc` — это «сначала самые давние ответы», а\nне «сначала неотвеченные»: неотвеченные забирайте `has_reply=false`.\n\n⚠️ Лента по папке из одной группы может содержать **больше** отзывов, чем\nлента по этой же группе: в групповом режиме закреплённые по умолчанию\nисключены (`show_pinned=false`), а в папочном закрепления не применяются\nвовсе и такие отзывы приходят обычными строками.","operationId":"get_reviews_v2_v2_reviews_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"folder_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Единая лента по всей папке: все группы папки и её подпапок. Взаимоисключим с `group_id` — ровно один из двух. Выключенные и удалённые группы в состав не входят. В папочном режиме закрепления не применяются (закреплённый отзыв приходит обычной строкой), а скрытия применяются по составу папки; флаги `is_hidden` / `is_pinned` / `pin_position` не отдаются.","title":"Folder Id"},"description":"Единая лента по всей папке: все группы папки и её подпапок. Взаимоисключим с `group_id` — ровно один из двух. Выключенные и удалённые группы в состав не входят. В папочном режиме закрепления не применяются (закреплённый отзыв приходит обычной строкой), а скрытия применяются по составу папки; флаги `is_hidden` / `is_pinned` / `pin_position` не отдаются."},{"name":"group_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Идентификатор группы. Взаимоисключим с `folder_id` — в запросе должен быть ровно один из двух.","title":"Group Id"},"description":"Идентификатор группы. Взаимоисключим с `folder_id` — в запросе должен быть ровно один из двух."},{"name":"link_ids","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"description":"Filter by link IDs.","default":[],"title":"Link Ids"},"description":"Filter by link IDs."},{"name":"source_ids","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"description":"Фильтр по площадкам: id из `GET /v2/sources`. Пусто — все площадки группы.","default":[],"title":"Source Ids"},"description":"Фильтр по площадкам: id из `GET /v2/sources`. Пусто — все площадки группы."},{"name":"sort_by","in":"query","required":false,"schema":{"enum":["date","rating","reply_date"],"type":"string","description":"Поле сортировки: `date` (по дате публикации), `rating` (оценка) или `reply_date` (дата ответа организации; отзывы без ответа — в конце списка при любом направлении).","default":"date","title":"Sort By"},"description":"Поле сортировки: `date` (по дате публикации), `rating` (оценка) или `reply_date` (дата ответа организации; отзывы без ответа — в конце списка при любом направлении)."},{"name":"sort_order","in":"query","required":false,"schema":{"enum":["desc","asc"],"type":"string","description":"Направление сортировки: `desc` / `asc`.","default":"desc","title":"Sort Order"},"description":"Направление сортировки: `desc` / `asc`."},{"name":"min_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":5,"minimum":0},{"type":"null"}],"description":"Минимальная оценка включительно, шкала 1–5 (дробное). Пусто — без нижней границы. `0` — без нижней границы и **вместе с отзывами без оценки**: обычный фильтр по оценке их не возвращает.","title":"Min Rating"},"description":"Минимальная оценка включительно, шкала 1–5 (дробное). Пусто — без нижней границы. `0` — без нижней границы и **вместе с отзывами без оценки**: обычный фильтр по оценке их не возвращает."},{"name":"max_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":5,"minimum":1},{"type":"null"}],"description":"Максимальная оценка включительно, шкала 1–5 (дробное). Пусто — без верхней границы.","title":"Max Rating"},"description":"Максимальная оценка включительно, шкала 1–5 (дробное). Пусто — без верхней границы."},{"name":"published_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Нижняя граница по дате публикации (`review_date`), включительно. Формат `YYYY-MM-DD`.","title":"Published From"},"description":"Нижняя граница по дате публикации (`review_date`), включительно. Формат `YYYY-MM-DD`."},{"name":"published_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Верхняя граница по дате публикации (`review_date`), включительно. Формат `YYYY-MM-DD`.","title":"Published To"},"description":"Верхняя граница по дате публикации (`review_date`), включительно. Формат `YYYY-MM-DD`."},{"name":"has_images","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Фильтр по наличию изображений.","title":"Has Images"},"description":"Фильтр по наличию изображений."},{"name":"has_videos","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Фильтр по наличию видео.","title":"Has Videos"},"description":"Фильтр по наличию видео."},{"name":"has_text","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Фильтр по наличию текста отзыва.","title":"Has Text"},"description":"Фильтр по наличию текста отзыва."},{"name":"has_reply","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Фильтр по наличию ответа организации.","title":"Has Reply"},"description":"Фильтр по наличию ответа организации."},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"description":"Поиск подстроки по тексту отзыва (регистронезависимо на проде; < 2 непробельных символов игнорируются).","title":"Search"},"description":"Поиск подстроки по тексту отзыва (регистронезависимо на проде; < 2 непробельных символов игнорируются)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Размер страницы (1–1000). По умолчанию 100.","default":100,"title":"Limit"},"description":"Размер страницы (1–1000). По умолчанию 100."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Смещение для пагинации.","default":0,"title":"Offset"},"description":"Смещение для пагинации."},{"name":"include_deleted","in":"query","required":false,"schema":{"type":"boolean","description":"Включать отзывы, помеченные удалёнными на площадке.","default":false,"title":"Include Deleted"},"description":"Включать отзывы, помеченные удалёнными на площадке."},{"name":"only_deleted","in":"query","required":false,"schema":{"type":"boolean","description":"Только удалённые отзывы (приоритет над include_deleted).","default":false,"title":"Only Deleted"},"description":"Только удалённые отзывы (приоритет над include_deleted)."},{"name":"show_hidden","in":"query","required":false,"schema":{"type":"boolean","description":"Показывать скрытые отзывы (с флагом is_hidden).","default":false,"title":"Show Hidden"},"description":"Показывать скрытые отзывы (с флагом is_hidden)."},{"name":"show_pinned","in":"query","required":false,"schema":{"type":"boolean","description":"Показывать закреплённые отзывы (с флагом is_pinned).","default":false,"title":"Show Pinned"},"description":"Показывать закреплённые отзывы (с флагом is_pinned)."},{"name":"separate_pos_neg","in":"query","required":false,"schema":{"type":"boolean","description":"Разбирать слитый текст на `positive`/`negative`.","default":false,"title":"Separate Pos Neg"},"description":"Разбирать слитый текст на `positive`/`negative`."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewsPageV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/reviews/{review_id}":{"get":{"tags":["reviews"],"summary":"Получить один отзыв по идентификатору","description":"Один отзыв по его идентификатору — та же модель, что в `GET /v2/reviews`.\n\nНужен там, где идентификатор уже на руках, а группы под ним нет: вебхук\nприносит `id` отзыва, усечённая доставка (`truncated: true`) отдаёт только\nего, и после публикации ответа проверить `reply_text` больше нечем.\n\nОтзыв чужого аккаунта и отзыв, которого нет, отвечают одинаково —\n`404 not_found`: иначе идентификатор стал бы способом узнавать о чужих\nданных. Видимость та же, что у ленты по умолчанию: удалённые на площадке и\nскрытые отзывы не отдаются, пробный потолок соблюдается.","operationId":"get_review_v2_v2_reviews__review_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"review_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded review ID","title":"Review Id"},"description":"Encoded review ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/reviews/sync":{"get":{"tags":["reviews"],"summary":"Синхронизация отзывов: поток изменений с курсором","description":"Отзывы, изменившиеся у нас после точки, на которой вы остановились.\n\nПервый запрос — с адресом объекта (`group_id` или `folder_id`), дальше\nдостаточно `cursor` из предыдущего ответа. Цикл клиента:\n\n```\nповторять запрос, пока has_next = true\nсохранить последний next_cursor у себя\nвернуться позже с ним же\n```\n\n`next_cursor` приходит **всегда** — в том числе на последней и на пустой\nстранице: это закладка, которую вы храните между прогонами. Обрыв связи\nстоит одной страницы, а не всего обхода.\n\nПорядок фиксирован (от давних изменений к свежим) и не настраивается,\nфильтров нет — иначе поток стал бы дырявым: отзыв, вышедший правкой за\nграницу фильтра, должен был бы прийти событием «выбыл», а такого события\nнет. Удалённые на площадке приходят всегда, с флагом `is_deleted`.\n\nОдин и тот же отзыв может прийти дважды, если он изменился во время\nобхода, — **применяйте upsert по `id`**, потерять строку поток не может.\n\nВыдача отстаёт от «сейчас» на несколько секунд: строка, у которой время\nуже проставлено, а транзакция ещё не завершена, иначе оказалась бы позади\nвашего курсора и не пришла бы никогда.","operationId":"sync_reviews_v2_v2_reviews_sync_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"folder_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Синхронизировать папку целиком: все группы папки и её подпапок. Взаимоисключим с `group_id` — ровно один из двух. Выключенные и удалённые группы в состав не входят.","title":"Folder Id"},"description":"Синхронизировать папку целиком: все группы папки и её подпапок. Взаимоисключим с `group_id` — ровно один из двух. Выключенные и удалённые группы в состав не входят."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Курсор из `next_cursor` предыдущего ответа. С ним остальные параметры можно не передавать — адресат, режим и размер страницы запечены внутри. Если всё же передаёте, они должны совпадать с запечёнными, иначе `400 cursor_mismatch`.","title":"Cursor"},"description":"Курсор из `next_cursor` предыдущего ответа. С ним остальные параметры можно не передавать — адресат, режим и размер страницы запечены внутри. Если всё же передаёте, они должны совпадать с запечёнными, иначе `400 cursor_mismatch`."},{"name":"mode","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(all|new)$"},{"type":"null"}],"description":"Что присылать:\n\n* `all` (по умолчанию) — и новые отзывы, и изменения. Полная актуальная копия.\n* `new` — только впервые появившиеся отзывы. Правки, ответы и пропажи не\n  приходят; каждый отзыв приходит один раз в жизни, апсерт не нужен.\n\nРежима «только изменения» нет намеренно: клиенту, который уже догнал текущее\nсостояние, режим `all` ровно его и отдаёт — новых пока нет, приходят одни\nправки.","title":"Mode"},"description":"Что присылать:\n\n* `all` (по умолчанию) — и новые отзывы, и изменения. Полная актуальная копия.\n* `new` — только впервые появившиеся отзывы. Правки, ответы и пропажи не\n  приходят; каждый отзыв приходит один раз в жизни, апсерт не нужен.\n\nРежима «только изменения» нет намеренно: клиенту, который уже догнал текущее\nсостояние, режим `all` ровно его и отдаёт — новых пока нет, приходят одни\nправки."},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":1000,"minimum":1},{"type":"null"}],"description":"Размер страницы (1–1000). По умолчанию 100. Запекается в курсор: дальше его можно не повторять. Переданный явно — перебивает запечённый.","title":"Limit"},"description":"Размер страницы (1–1000). По умолчанию 100. Запекается в курсор: дальше его можно не повторять. Переданный явно — перебивает запечённый."},{"name":"group_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Идентификатор группы. Взаимоисключим с `folder_id` — в запросе должен быть ровно один из двух.","title":"Group Id"},"description":"Идентификатор группы. Взаимоисключим с `folder_id` — в запросе должен быть ровно один из двух."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewsSyncPageV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/groups":{"post":{"tags":["groups"],"summary":"Создать группу","description":"Создать группу — новый объект, по которому вы получаете отзывы,\nаналитику и (опционально) вебхуки.\n\nГруппа — основная единица адресации в API: `GET /v2/reviews`,\n`GET /v2/analytics` и подписки `POST /v2/webhooks` работают через\n`group_id`. Внутри группы — ссылки: по одной на каждую площадку (Яндекс\nКарты, Google, 2ГИС, Островок и т.д.), которые сервис сам периодически\nопрашивает и собирает из них отзывы.\n\nСсылки можно передать сразу (`links`, до 200 штук за раз) или не\nпередавать вообще — группу можно создать пустой и добавить их позже через\n`POST /v2/groups/{group_id}/links`. Площадка для каждой ссылки\nопределяется автоматически по домену URL — указывать её отдельно не нужно;\nдля новых ссылок сбор отзывов запускается сразу после создания.\n\nГруппа занимает место в лимите объектов аккаунта (`max_active_groups`) с\nмомента создания — при исчерпании лимита ответ `403`. `link_results` в\nответе показывает итог по каждой переданной ссылке отдельно: успех с\n`link_id` или `error` (например, домен не опознан, или в группе уже есть\nссылка на эту площадку) — часть ссылок может не добавиться, но сама группа\nпри этом всё равно создаётся.","operationId":"create_group_v2_v2_groups_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupCreateV2"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupCreateResponseV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"tags":["groups"],"summary":"Список групп","description":"Список групп с пагинацией. Порядок — `id` по убыванию.","operationId":"list_groups_v2_v2_groups_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Размер страницы (1–1000). По умолчанию 100.","default":100,"title":"Limit"},"description":"Размер страницы (1–1000). По умолчанию 100."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Смещение для пагинации.","default":0,"title":"Offset"},"description":"Смещение для пагинации."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page_GroupV2_"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"tags":["groups"],"summary":"Включить/выключить группы","description":"Массово включить или выключить группы — то же поле `is_active`, что и в\n`PUT /v2/groups/{group_id}`, применённое к набору за один вызов.\n\n**Переключается всё, что можно переключить.** `group_id`, недоступный\nвызывающему (чужой, несуществующий, удалённый), молча пропускается —\nостальные группы набора переключаются как обычно. Если не нашлось ни одной,\nответ — пустой `data`. Лимит объектов переключение не трогает: слот в нём\nзанимает существование группы, а не её состояние.\n\nГруппы, уже находящиеся в нужном состоянии, пропускаются молча: в историю\n(`group_lifecycle`) пишутся только фактические переключения. Ответ —\n`data` с найденными группами в переданном порядке и с итоговым `is_active`;\nвключая те, что уже были в нужном состоянии. Пропущенные id в ответе просто\nотсутствуют — их видно по разнице между отправленным и вернувшимся.\n\nСемантика выключения — как у точечной ручки: группа не отдаёт отзывы,\nаналитику, скрытые и закреплённые и не собирается, но остаётся в списке,\nредактируется, удаляется и продолжает занимать место в лимите объектов.","operationId":"switch_groups_v2_v2_groups_put","security":[{"APIKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupsSwitchV2"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnAll_GroupV2_"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/groups/{group_id}":{"get":{"tags":["groups"],"summary":"Получить группу","description":"Получить одну группу целиком — с составом ссылок и статусом сбора по каждой.\n\nЗдесь же видно `is_active` — общий выключатель группы (переключается\nчерез `PUT /v2/groups/{group_id}`). Он не влияет на доступность самой\nгруппы: выключенная группа всё равно возвращается этим методом, но\n`GET /v2/reviews` и `GET /v2/analytics` по ней в этом состоянии отвечают `404`.","operationId":"get_group_v2_v2_groups__group_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"tags":["groups"],"summary":"Обновить группу","description":"Обновить группу: название (`name`) и/или выключатель (`is_active`).\n\n**`is_active=false`** выключает группу: `GET /v2/reviews` и `GET /v2/analytics`\nпо ней отвечают 404, ссылки перестают собираться. Сама группа остаётся —\nвидна в списке, переименовывается, ссылки правятся и удаляются,\n`DELETE` работает.\n\n**Лимит объектов переключение не трогает.** Слот в `max_active_groups`\nзанимает существование группы, а не её состояние: место освобождает только\nудаление, занимает — только создание. Поэтому включить обратно можно всегда.\n\nКаждое фактическое переключение пишется в историю группы (`group_lifecycle`,\nсобытия `activated` / `deactivated`); повторная установка того же значения\nничего не пишет.","operationId":"update_group_v2_v2_groups__group_id__put","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupUpdateV2"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["groups"],"summary":"Удалить группу","description":"Удалить группу — мягкое удаление.\n\nГруппа перестаёт быть видна в `GET /v2/groups` и во всех остальных\nметодах (`404` при обращении по `id` напрямую), её ссылки перестают\nсобираться, но уже собранные отзывы физически не удаляются. Действие\nнеобратимо через API (отмены удаления нет) и, в отличие от простого\nвыключения через `PUT /v2/groups/{group_id}` (`is_active=false`),\nосвобождает место в лимите объектов аккаунта (`max_active_groups`).","operationId":"delete_group_v2_v2_groups__group_id__delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupDeleteResponse"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/groups/{group_id}/links":{"post":{"tags":["links"],"summary":"Добавить ссылки","description":"Добавить одну или несколько ссылок в существующую группу.\n\nСсылка — это URL страницы объекта на одной конкретной площадке (Яндекс\nКарты, Google, 2ГИС и т.д.); площадка определяется автоматически по\nдомену URL — указывать её отдельно не нужно. **Один слот на площадку**: если в\nгруппе уже есть ссылка на этот источник, добавление новой — ошибка по\nэтому элементу (`error` в `link_results`), но остальные переданные ссылки\nпри этом всё равно обрабатываются. Для только что добавленных ссылок сбор\nотзывов запускается немедленно, а не по расписанию.\n\nПринимает как один объект, так и массив (до 200 ссылок за раз).","operationId":"add_links_v2_v2_groups__group_id__links_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/LinkInputV2"},{"type":"array","items":{"$ref":"#/components/schemas/LinkInputV2"}}],"title":"Links"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkLinksResponse"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"tags":["links"],"summary":"Список ссылок","description":"Все ссылки группы — URL, площадка и статус последнего сбора по каждой.\n\nReturn-all (`{data}`, без пагинации): в одной группе не может быть больше\nссылок, чем зарегистрировано площадок, так что пагинация не нужна.\n`scrape_status`/`scrape_error`/`last_scraped_at` показывают, когда и\nуспешно ли последний раз опрашивалась именно эта ссылка — по ним видно,\nпочему по конкретной площадке пока нет отзывов.","operationId":"list_links_v2_v2_groups__group_id__links_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnAll_LinkV2_"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/groups/{group_id}/links/{link_id}":{"put":{"tags":["links"],"summary":"Обновить ссылку","description":"Изменить настройки одной ссылки внутри группы.\n\n`link_name` — как ссылка называется у вас (например, «Филиал на\nНевском»); если не задано, показывается название, которое мы определили\nсами по ссылке (`auto_name`). `is_active` — включает или выключает\nавтосбор именно по этой ссылке, не удаляя её: `false` оставляет ссылку в\nгруппе и в ответах, но новые отзывы по ней собираться перестают.","operationId":"update_link_v2_v2_groups__group_id__links__link_id__put","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"},{"name":"link_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded link ID","title":"Link Id"},"description":"Encoded link ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["links"],"summary":"Удалить ссылку","description":"Удалить ссылку из группы — площадка перестаёт быть частью отзывов и\nаналитики этой группы.\n\nУдаляется сама привязка ссылки к группе, а не уже собранные отзывы: они\nостаются в базе, но больше не отдаются через эту группу. Действие\nнеобратимо; если у отзывов этой ссылки были пометки «скрыто» или\n«закреплено» в рамках группы, они автоматически снимаются — иначе\nостались бы указывать на ссылку, которой в группе больше нет.","operationId":"remove_link_v2_v2_groups__group_id__links__link_id__delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"},{"name":"link_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded link ID","title":"Link Id"},"description":"Encoded link ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkRemoveResponse"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/groups/{group_id}/hidden-reviews":{"post":{"tags":["hidden & pinned"],"summary":"Скрыть отзывы в группе","description":"Скрыть отзывы в группе — списком id.\n\nСкрытый отзыв перестаёт попадать в `GET /v2/reviews` по умолчанию (нужен\n`show_hidden=true`, чтобы его увидеть), но физически никуда не удаляется —\nэто пометка показа для конкретной группы, обратимая через\n`DELETE .../hidden-reviews`. Типичный кейс — модерация: убрать отзыв из\nпоказа, не трогая площадку, откуда он собран. Уже скрытый отзыв можно скрыть\nповторно без ошибки (идемпотентно). Закреплённый отзыв при скрытии\nавтоматически открепляется — быть одновременно в двух состояниях показа\nнельзя.\n\n`GET /v2/analytics` скрытые отзывы **продолжает считать**: средний балл и\nраспределение оценок берутся по всем собранным отзывам группы. Скрытие\nоформляет показ, а не правит данные.","operationId":"hide_reviews_v2_v2_groups__group_id__hidden_reviews_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HiddenReviewsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HiddenReviewsMutationResponse"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["hidden & pinned"],"summary":"Вернуть отзывы из скрытых","description":"Вернуть отзывы из скрытых — списком id.\n\nОтменяет `POST .../hidden-reviews` для перечисленных отзывов: они снова\nпоявляются в `GET /v2/reviews` без `show_hidden=true` и снова учитываются\nв `GET /v2/analytics`. Отзыв, который не был скрыт, пропускается без ошибки.","operationId":"unhide_reviews_v2_v2_groups__group_id__hidden_reviews_delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HiddenReviewsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HiddenReviewsMutationResponse"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"tags":["hidden & pinned"],"summary":"Список скрытых отзывов группы","description":"Список скрытых отзывов группы — с теми же фильтрами, сортировкой и\nпагинацией, что и `GET /v2/reviews` (конверт `Page`). Полезно для экрана\nмодерации, где нужно показать именно то, что скрыто, и дать вернуть обратно.","operationId":"list_hidden_reviews_v2_v2_groups__group_id__hidden_reviews_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"},{"name":"link_ids","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"description":"Filter by link IDs.","default":[],"title":"Link Ids"},"description":"Filter by link IDs."},{"name":"source_ids","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"default":[],"title":"Source Ids"}},{"name":"sort_by","in":"query","required":false,"schema":{"enum":["date","rating","reply_date"],"type":"string","default":"date","title":"Sort By"}},{"name":"sort_order","in":"query","required":false,"schema":{"enum":["desc","asc"],"type":"string","default":"desc","title":"Sort Order"}},{"name":"min_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":5,"minimum":1},{"type":"null"}],"title":"Min Rating"}},{"name":"max_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":5,"minimum":1},{"type":"null"}],"title":"Max Rating"}},{"name":"published_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Published From"}},{"name":"published_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Published To"}},{"name":"has_images","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Images"}},{"name":"has_text","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Text"}},{"name":"has_reply","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Reply"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Search"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"include_deleted","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Deleted"}},{"name":"only_deleted","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Only Deleted"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page_ReviewV2_"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/groups/{group_id}/pinned-reviews":{"put":{"tags":["hidden & pinned"],"summary":"Задать порядок закреплённых отзывов","description":"Задать порядок закреплённых отзывов — полная замена списка целиком, не добавление.\n\nЗакреплённый отзыв получает флаг `is_pinned` и `pin_position` в\n`GET /v2/reviews` и обычно используется, чтобы вручную поднять\nпонравившиеся отзывы в начало выдачи — независимо от сортировки\nпо дате или рейтингу. Порядок в `ordered_review_ids` — это и есть порядок\nпоказа (`pin_position` 0, 1, 2, …). Максимум 30 закреплённых на группу;\nпустой список открепляет все. Возвращает актуальный конфиг целиком\n(return-all `{data}`, R2).","operationId":"set_pinned_reviews_v2_v2_groups__group_id__pinned_reviews_put","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PinnedReorderRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnAll_ReviewV2_"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["hidden & pinned"],"summary":"Закрепить отзывы (в конец)","description":"Закрепить отзывы, добавив их в конец текущего списка (не заменяя его).\n\nПроще, чем `PUT .../pinned-reviews`, когда нужно просто добавить пару\nновых отзывов, не пересылая весь список целиком. Максимум 30 закреплённых\nна группу — при превышении лимита `400 pinned_limit_exceeded`. Уже\nзакреплённый отзыв можно передать повторно без ошибки — порядок\nостальных не меняется.","operationId":"add_pinned_reviews_v2_v2_groups__group_id__pinned_reviews_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PinnedAddRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnAll_ReviewV2_"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["hidden & pinned"],"summary":"Открепить отзывы","description":"Открепить перечисленные отзывы; остальные закреплённые сохраняют\nвзаимный порядок и `pin_position` пересчитывается без пропусков.","operationId":"remove_pinned_reviews_v2_v2_groups__group_id__pinned_reviews_delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PinnedAddRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnAll_ReviewV2_"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"tags":["hidden & pinned"],"summary":"Текущий конфиг закрепления","description":"Текущий конфиг закрепления — список закреплённых отзывов группы в\nпорядке показа (`pin_position`), с теми же фильтрами и пагинацией, что и\n`GET /v2/reviews` (конверт `Page`). Без `sort_by` порядок — именно порядок\nзакрепления; с `sort_by` пересортировывает найденные закреплённые отзывы.","operationId":"list_pinned_reviews_v2_v2_groups__group_id__pinned_reviews_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded group ID","title":"Group Id"},"description":"Encoded group ID"},{"name":"link_ids","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"description":"Filter by link IDs.","default":[],"title":"Link Ids"},"description":"Filter by link IDs."},{"name":"source_ids","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"default":[],"title":"Source Ids"}},{"name":"sort_by","in":"query","required":false,"schema":{"anyOf":[{"enum":["date","rating","reply_date"],"type":"string"},{"type":"null"}],"title":"Sort By"}},{"name":"sort_order","in":"query","required":false,"schema":{"enum":["desc","asc"],"type":"string","default":"desc","title":"Sort Order"}},{"name":"min_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":5,"minimum":1},{"type":"null"}],"title":"Min Rating"}},{"name":"max_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":5,"minimum":1},{"type":"null"}],"title":"Max Rating"}},{"name":"published_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Published From"}},{"name":"published_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Published To"}},{"name":"has_images","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Images"}},{"name":"has_text","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Text"}},{"name":"has_reply","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Reply"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Search"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"include_deleted","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Deleted"}},{"name":"only_deleted","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Only Deleted"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page_ReviewV2_"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/folders":{"post":{"tags":["folders"],"summary":"Создать папку","description":"Создать папку — сразу с составом и, если нужно, внутри другой папки.\n\nПапка — способ спросить отзывы и аналитику сразу по нескольким объектам:\n`GET /v2/reviews?folder_id=…` отдаёт единую ленту по всем группам папки и\nеё подпапок. Глубина — два уровня: папка → подпапка → группы. Одна группа\nможет лежать в скольких угодно папках.\n\nПапки **не тарифицируются** и полностью доступны в пробном периоде: место в\nлимите объектов занимают группы, а не папки. Ограничение одно — не больше\n1000 живых папок на аккаунт (`403 folder_limit_reached`); удалённые не в счёт.\n\nГруппы из `group_ids`, которые вам не принадлежат, удалены или не\nсуществуют, папку создать не мешают: они просто не попадают в состав, а их\nидентификаторы возвращаются в `ignored_group_ids`.\n\nПовторный вызов с тем же заголовком `Idempotency-Key` вернёт тот же ответ и\nне создаст вторую папку — уникальности названий здесь нет, так что на\nтаймауте клиента это единственная защита от дубля.\n\nОшибки: `403 folder_limit_reached`, `400 folder_depth_exceeded`\n(родитель сам вложен), `404 folder_not_found` (родитель не найден).","operationId":"create_folder_v2_v2_folders_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderCreateV2"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderWriteV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"tags":["folders"],"summary":"Все папки с подпапками и группами","description":"Дерево ваших папок целиком: папки, их подпапки и группы в них.\n\nВ `data` — только корневые папки. Подпапки лежат внутри своего родителя, в\nполе `subfolders`; глубина ограничена двумя уровнями, поэтому у подпапки\n`subfolders` всегда пустой. Отдельного запроса на вложенность не нужно.\nПагинации нет, конверт — `{data}`.\n\nПорядок и на верхнем уровне, и среди подпапок — по названию по возрастанию,\nпри совпадении — по `id`. Сортировка **регистронезависимая**: коллация базы\n`utf8mb4_*_ci`.\n\nСостав приходит объектами `{id, name}`, чтобы не ходить за названиями\nвторым запросом, и у каждой папки он **свой**: группы подпапки в `groups`\nродителя не попадают, хотя в выдачу по `folder_id` родителя войдут.\nПоказан именно состав: выключенная группа в нём остаётся, хотя отзывов и\nаналитики по `folder_id` не даёт.","operationId":"list_folders_v2_v2_folders_get","security":[{"APIKeyHeader":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnAll_FolderV2_"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/folders/{folder_id}":{"get":{"tags":["folders"],"summary":"Папка со всем, что в ней лежит","description":"Одна папка: её группы и её подпапки со своими группами.\n\nТот же объект, что и в списке, — но только запрошенная ветка. Годится и для\nподпапки: тогда придёт она сама (с `parent_folder_id` родителя) и пустой\n`subfolders`.\n\nГрупп родителя в подпапке не будет и наоборот: `groups` каждой папки — её\nсобственный состав. Чтобы получить отзывы или аналитику сразу по всей\nветке, передайте `folder_id` в `GET /v2/reviews` или `GET /v2/analytics` —\nтам группы папки и её подпапок объединяются.\n\nОшибки: `404 folder_not_found` (папки нет, она удалена или чужая).","operationId":"get_folder_v2_v2_folders__folder_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"folder_id","in":"path","required":true,"schema":{"type":"integer","description":"Идентификатор папки (обычное число).","title":"Folder Id"},"description":"Идентификатор папки (обычное число)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"tags":["folders"],"summary":"Изменить папку","description":"Переименовать, перенести и сменить состав — одним вызовом.\n\nМеняется только то, что передано: поля нет — не трогаем.\n`parent_folder_id: null` выносит папку в корень.\n\n⚠️ `group_ids` — **полная замена** состава, а не добавление: пришедший\nсписок становится составом целиком, `[]` очищает. Группы, которые вам не\nпринадлежат, удалены или не существуют, пропускаются: состав становится\nтем, что из списка удалось положить, а пропущенные идентификаторы\nвозвращаются в `ignored_group_ids`.\n\nВозвращает папку целиком, вместе с её подпапками.\n\nОшибки: `400 folder_depth_exceeded` (родитель сам вложен, у папки есть свои\nподпапки, или папку делают родителем самой себе), `404 folder_not_found`\n(папка или родитель не найдены).","operationId":"update_folder_v2_v2_folders__folder_id__patch","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"folder_id","in":"path","required":true,"schema":{"type":"integer","description":"Идентификатор папки (обычное число).","title":"Folder Id"},"description":"Идентификатор папки (обычное число)."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderUpdateV2"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderWriteV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["folders"],"summary":"Удалить папку","description":"Удалить папку. Группы внутри остаются жить и продолжают собираться.\n\nУдаляется только сама папка — ни одна группа не удаляется и не выключается.\nЕсли у папки есть живые подпапки — `409 folder_not_empty`: удалите или\nперенесите их сначала. Уже удалённая подпапка удалению родителя не мешает.\n\nПовторное удаление той же папки — `404`.","operationId":"delete_folder_v2_v2_folders__folder_id__delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"folder_id","in":"path","required":true,"schema":{"type":"integer","description":"Идентификатор папки (обычное число).","title":"Folder Id"},"description":"Идентификатор папки (обычное число)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderDeleteResponse"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/analytics":{"get":{"tags":["analytics"],"summary":"Сводная аналитика группы или папки","description":"Сводная аналитика по отзывам за период — по группе или по папке целиком.\n\nАдресовать нужно **ровно один** объект: `group_id` или `folder_id`. Оба\nсразу — `400 group_and_folder_conflict`, ни одного — `400 group_or_folder_required`.\n\nВ папочном режиме считается по всем группам папки и её подпапок; ссылка,\nлежащая в нескольких группах папки, учитывается один раз. `by_source`\nсхлопывает одну площадку разных объектов в одну строку с суммой.\n`meta.group_ids` показывает фактический состав, попавший в расчёт.","operationId":"get_analytics_v2_v2_analytics_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"folder_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Аналитика по всей папке: все группы папки и её подпапок. Взаимоисключим с `group_id` — ровно один из двух. Выключенные и удалённые группы в состав не входят; `by_source` схлопывает одну площадку разных объектов в одну строку с суммой.","title":"Folder Id"},"description":"Аналитика по всей папке: все группы папки и её подпапок. Взаимоисключим с `group_id` — ровно один из двух. Выключенные и удалённые группы в состав не входят; `by_source` схлопывает одну площадку разных объектов в одну строку с суммой."},{"name":"group_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Идентификатор группы. Взаимоисключим с `folder_id` — в запросе должен быть ровно один из двух.","title":"Group Id"},"description":"Идентификатор группы. Взаимоисключим с `folder_id` — в запросе должен быть ровно один из двух."},{"name":"source_ids","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"description":"Фильтр по площадкам (id из `GET /v2/sources`). Пусто — все площадки группы.","default":[],"title":"Source Ids"},"description":"Фильтр по площадкам (id из `GET /v2/sources`). Пусто — все площадки группы."},{"name":"published_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Начало периода по дате публикации, включительно (`YYYY-MM-DD`). По умолчанию `2000-01-01`.","title":"Published From"},"description":"Начало периода по дате публикации, включительно (`YYYY-MM-DD`). По умолчанию `2000-01-01`."},{"name":"published_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Конец периода по дате публикации, включительно (`YYYY-MM-DD`). По умолчанию сегодня.","title":"Published To"},"description":"Конец периода по дате публикации, включительно (`YYYY-MM-DD`). По умолчанию сегодня."},{"name":"min_rating","in":"query","required":false,"schema":{"type":"number","maximum":5,"minimum":0,"description":"Нижняя граница оценки (1–5, дробное), включительно. По умолчанию 1. `0` — без нижней границы и **вместе с отзывами без оценки**: обычный фильтр по оценке их не учитывает.","default":1,"title":"Min Rating"},"description":"Нижняя граница оценки (1–5, дробное), включительно. По умолчанию 1. `0` — без нижней границы и **вместе с отзывами без оценки**: обычный фильтр по оценке их не учитывает."},{"name":"max_rating","in":"query","required":false,"schema":{"type":"number","maximum":5,"minimum":1,"description":"Верхняя граница оценки (1–5, дробное), включительно. По умолчанию 5.","default":5,"title":"Max Rating"},"description":"Верхняя граница оценки (1–5, дробное), включительно. По умолчанию 5."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsResponseV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/analytics/timeseries":{"get":{"tags":["analytics"],"summary":"Динамика отзывов по периодам","description":"Временные ряды метрик по отзывам — по группе или по папке целиком.\n\nАдресовать нужно **ровно один** объект: `group_id` или `folder_id`.\nВ папочном режиме ряд строится по всем группам папки и её подпапок.","operationId":"get_analytics_timeseries_v2_v2_analytics_timeseries_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"folder_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Аналитика по всей папке: все группы папки и её подпапок. Взаимоисключим с `group_id` — ровно один из двух. Выключенные и удалённые группы в состав не входят; `by_source` схлопывает одну площадку разных объектов в одну строку с суммой.","title":"Folder Id"},"description":"Аналитика по всей папке: все группы папки и её подпапок. Взаимоисключим с `group_id` — ровно один из двух. Выключенные и удалённые группы в состав не входят; `by_source` схлопывает одну площадку разных объектов в одну строку с суммой."},{"name":"group_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Идентификатор группы. Взаимоисключим с `folder_id` — в запросе должен быть ровно один из двух.","title":"Group Id"},"description":"Идентификатор группы. Взаимоисключим с `folder_id` — в запросе должен быть ровно один из двух."},{"name":"source_ids","in":"query","required":false,"schema":{"type":"array","items":{"type":"integer"},"description":"Площадки для разбивки; `0` — агрегированный ряд. По умолчанию `[0]`.","default":[],"title":"Source Ids"},"description":"Площадки для разбивки; `0` — агрегированный ряд. По умолчанию `[0]`."},{"name":"granularity","in":"query","required":false,"schema":{"enum":["day","week","month"],"type":"string","description":"Размер интервала: `day` / `week` / `month`.","default":"month","title":"Granularity"},"description":"Размер интервала: `day` / `week` / `month`."},{"name":"published_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Начало периода по дате публикации, включительно (`YYYY-MM-DD`). По умолчанию 90 дней назад.","title":"Published From"},"description":"Начало периода по дате публикации, включительно (`YYYY-MM-DD`). По умолчанию 90 дней назад."},{"name":"published_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Конец периода по дате публикации, включительно (`YYYY-MM-DD`). По умолчанию сегодня.","title":"Published To"},"description":"Конец периода по дате публикации, включительно (`YYYY-MM-DD`). По умолчанию сегодня."},{"name":"min_rating","in":"query","required":false,"schema":{"type":"number","maximum":5,"minimum":0,"description":"Нижняя граница оценки (1–5, дробное). По умолчанию 1. `0` — без нижней границы и **вместе с отзывами без оценки**.","default":1,"title":"Min Rating"},"description":"Нижняя граница оценки (1–5, дробное). По умолчанию 1. `0` — без нижней границы и **вместе с отзывами без оценки**."},{"name":"max_rating","in":"query","required":false,"schema":{"type":"number","maximum":5,"minimum":1,"description":"Верхняя граница оценки (1–5, дробное). По умолчанию 5.","default":5,"title":"Max Rating"},"description":"Верхняя граница оценки (1–5, дробное). По умолчанию 5."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeseriesResponseV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/sources":{"get":{"tags":["sources"],"summary":"Список площадок","description":"Список площадок, с которых Rewio собирает отзывы — Яндекс Карты,\nGoogle, 2ГИС и ещё почти три десятка сервисов.\n\nОбычно сюда заглядывают один раз: найти `id` нужной площадки и\nиспользовать его в фильтрах у отзывов и аналитики. Поле\n`can_publish_reply` показывает, где мы умеем публиковать ваши ответы.","operationId":"list_sources_v2_v2_sources_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnAll_SourceV2_"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v2/webhooks":{"get":{"tags":["webhooks"],"summary":"Список вебхуков","description":"Список всех вебхуков аккаунта (return-all `{data}`).\n\nСекрета в ответе нет: он общий на аккаунт, а не на вебхук, и отдаётся\nотдельно через `GET /v2/webhooks/secret`.","operationId":"list_webhooks_v2_v2_webhooks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReturnAll_WebhookV2_"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"APIKeyHeader":[]}]},"post":{"tags":["webhooks"],"summary":"Создать вебхук","description":"Создать вебхук — подписку на пуш новых отзывов и ответов на ваш\nHTTPS-адрес вместо периодического опроса `GET /v2/reviews`.\n\nПри наступлении события сервис шлёт `POST` на `target` с телом-дайджестом\n(пачкой отзывов за раз, не по одному) и HMAC-подписью в заголовке\n`X-Hrev-Signature`. `event_types` выбирает, на что подписаться:\n`reviews.new` (новые отзывы), `replies.new` (у отзыва появился ответ\nорганизации), `scrape.run.finished` (обход завершился — «пульс», даже\nесли нового ничего не нашлось). `group_ids` — какие группы отслеживать\n(не передано — все ваши группы; папки здесь не поддерживаются,\nперечисляйте группы явно). `bucket_window` управляет частотой доставки:\n`immediate` — сразу после каждого обхода, `daily_<час>` — один дайджест в\nсутки в указанный час UTC. `filters` сужает, какие именно отзывы попадают\nв доставку (по оценке, площадке, наличию текста и картинок).\n\nПодпись всех ваших вебхуков использует **один секрет на аккаунт** —\nзаберите его через `GET /v2/webhooks/secret` (виден всегда, не только при\nсоздании). Полный формат тела доставки и проверка подписи — в гайде\n`/docs/guides/webhooks`.","operationId":"create_webhook_v2_v2_webhooks_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreateV2"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v2/webhooks/secret":{"get":{"tags":["webhooks"],"summary":"Секрет подписи аккаунта","description":"Текущий секрет подписи вебхуков (**один на аккаунт**, виден всегда).\nИспользуйте его для проверки `X-Hrev-Signature`. Требует ключ со scope\n`full`.","operationId":"get_account_secret_v2_v2_webhooks_secret_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountSecretResponse"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v2/webhooks/rotate-secret":{"post":{"tags":["webhooks"],"summary":"Перевыпустить секрет аккаунта","description":"Перевыпустить секрет аккаунта. Старый действует ещё 24ч (окно ротации):\nдоставки в это время подписываются ОБОИМИ секретами, чтобы вы успели\nобновить проверку без потери доставок.","operationId":"rotate_account_secret_v2_v2_webhooks_rotate_secret_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountSecretResponse"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v2/webhooks/{webhook_id}":{"patch":{"tags":["webhooks"],"summary":"Изменить вебхук","description":"Изменить URL, группы, события, фильтры или окно доставки — либо\nпоставить на паузу (`is_active: false`) без удаления. Меняются только\nпереданные поля. Пауза удобна, если ваш сервер временно недоступен:\nдоставки не копятся и не уходят в ретраи — подписка просто не\nсрабатывает, пока вы не включите её снова.","operationId":"update_webhook_v2_v2_webhooks__webhook_id__patch","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","title":"Webhook Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookUpdateV2"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["webhooks"],"summary":"Удалить вебхук","description":"Удалить вебхук вместе с историей его доставок. Действие необратимо;\nсекрет подписи аккаунта не затрагивается — он общий для всех вебхуков.","operationId":"delete_webhook_v2_v2_webhooks__webhook_id__delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","title":"Webhook Id"}}],"responses":{"204":{"description":"Successful Response"},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/webhooks/{webhook_id}/test":{"post":{"tags":["webhooks"],"summary":"Тестовая доставка","description":"Отправить тестовую доставку на `target` вебхука с настоящей\nHMAC-подписью — проверить приём и код ответа, не дожидаясь реального\nнового отзыва. Требует активный вебхук (`409 webhook_inactive`, если\nстоит на паузе). Ответ `202` — отправка асинхронная: доставку смотрите\nпо приёму на своей стороне.","operationId":"test_webhook_v2_v2_webhooks__webhook_id__test_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","title":"Webhook Id"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestSubscriptionResponse"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v2/support/message":{"post":{"tags":["support"],"summary":"Написать в поддержку","description":"Отправить обращение в поддержку.\n\nРаботает с любым ключом, включая `read_only`, и из личного кабинета.\n\nОтвет `202`: письмо принято к отправке. Мы отвечаем на почту аккаунта —\nили на то, что вы указали в `contact`.","operationId":"send_support_message_v2_support_message_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportMessage"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportAccepted"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v2/reviews/{review_id}/reply":{"put":{"tags":["replies"],"summary":"Опубликовать или заменить ответ","description":"Опубликовать ответ на отзыв или заменить уже стоящий.\n\nПубликация ответов входит в тариф «Полный»; на других тарифах ручка\n    отвечает `402 reply_requires_upgrade`.\n\n    Ручка идемпотентна: ответ на отзыв ровно один, повторный `PUT` тем же\n    текстом — не ошибка и не дубль. Публикация всегда асинхронная, поэтому\n    ответ `202`: опубликованный ответ появится в самом отзыве (`reply_text`)\n    и придёт событием `replies.new`, если у вас настроен вебхук.","operationId":"put_reply_v2_reviews__review_id__reply_put","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"review_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded review ID","title":"Review Id"},"description":"Encoded review ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyWriteV2"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["replies"],"summary":"Удалить ответ","description":"Удалить ваш ответ с площадки.\n\nУдаление, как и публикация, асинхронное: `202` означает, что мы приняли\nзадачу, а `status` дойдёт до `deleted`, когда площадка подтвердит.","operationId":"delete_reply_v2_reviews__review_id__reply_delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"review_id","in":"path","required":true,"schema":{"type":"integer","description":"Encoded review ID","title":"Review Id"},"description":"Encoded review ID"}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyV2"}}}},"default":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"schemas":{"AccountSecretResponse":{"properties":{"secret":{"type":"string","title":"Secret"},"rotated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Rotated At"}},"type":"object","required":["secret"],"title":"AccountSecretResponse","description":"Account-level webhook signing secret. Returned by `GET\n/webhooks/secret` (always) and `POST /webhooks/rotate-secret`.","examples":[{"secret":"whsec_..."}],"example":{"secret":"whsec_..."}},"AnalyticsMetaV2":{"properties":{"group_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Group Id","description":"Идентификатор группы, по которой посчитаны данные. В папочном режиме — `null`. Поле **всегда присутствует** в ответе: клиенты, сгенерированные из OpenAPI, ломаются на исчезающих полях."},"folder_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Folder Id","description":"Идентификатор папки, если запрос был по папке. Иначе `null`."},"group_ids":{"items":{"type":"integer"},"type":"array","title":"Group Ids","description":"Фактический состав, по которому посчитаны данные: в групповом режиме — одна группа, в папочном — все группы папки и её подпапок, участвовавшие в расчёте (выключенные и удалённые сюда не попадают).","default":[]},"published_from":{"type":"string","format":"date","title":"Published From","description":"Фактически применённое начало периода по дате публикации, включительно."},"published_to":{"type":"string","format":"date","title":"Published To","description":"Фактически применённый конец периода по дате публикации, включительно."},"source_ids":{"items":{"type":"integer"},"type":"array","title":"Source Ids","description":"Фактически применённый фильтр по площадкам. Пустой список — «все площадки группы»."},"generated_at":{"type":"string","format":"date-time","title":"Generated At","description":"Момент расчёта данных (UTC, ISO 8601)."}},"type":"object","required":["published_from","published_to","source_ids","generated_at"],"title":"AnalyticsMetaV2","description":"Метаданные аналитики (/v2).","examples":[{"generated_at":"2026-07-30T05:33:12.210830Z","group_id":20,"group_ids":[20],"published_from":"2000-01-01","published_to":"2026-07-30","source_ids":[1,2,3,104,105,106,108]}],"example":{"generated_at":"2026-07-30T05:33:12.210830Z","group_id":20,"group_ids":[20],"published_from":"2000-01-01","published_to":"2026-07-30","source_ids":[1,2,3,104,105,106,108]}},"AnalyticsResponseV2":{"properties":{"meta":{"$ref":"#/components/schemas/AnalyticsMetaV2"},"summary":{"$ref":"#/components/schemas/AnalyticsSummary"},"by_source":{"items":{"$ref":"#/components/schemas/BySourceItem"},"type":"array","title":"By Source"},"access":{"anyOf":[{"$ref":"#/components/schemas/TrialAccessInfo"},{"type":"null"}],"description":"Ограничение выдачи. Присутствует только когда пробному доступу показали не всё — у обычного клиента ключа нет. Пробный срез применяется ДО подсчёта, поэтому без этого поля урезанные числа выглядят полными."}},"type":"object","required":["meta","summary","by_source"],"title":"AnalyticsResponseV2","description":"Ответ `GET /v2/analytics`.","examples":[{"by_source":[{"avg_rating":4.52,"latest_review_date":"2026-07-29","rating_distribution":{"1":147,"2":69,"3":164,"4":457,"5":2458},"reply_distribution":{"1":0,"2":0,"3":0,"4":0,"5":0},"review_count":3295,"reviews_with_images":207,"reviews_with_text":1723,"source_id":2,"source_name":"Google Reviews","total_with_reply":0},{"avg_rating":4.51,"latest_review_date":"2026-07-29","rating_distribution":{"1":50,"2":28,"3":62,"4":113,"5":803},"reply_distribution":{"1":45,"2":24,"3":59,"4":111,"5":770},"review_count":1056,"reviews_with_images":331,"reviews_with_text":1056,"source_id":1,"source_name":"YandexMaps","total_with_reply":1009},{"avg_rating":4.54,"latest_review_date":"2026-07-29","rating_distribution":{"1":5,"2":13,"3":85,"4":293,"5":283},"reply_distribution":{"1":1,"2":5,"3":29,"4":67,"5":44},"review_count":680,"reviews_with_images":58,"reviews_with_text":462,"source_id":106,"source_name":"Ostrovok","total_with_reply":146},{"avg_rating":4.46,"latest_review_date":"2026-07-30","rating_distribution":{"1":29,"2":11,"3":21,"4":43,"5":327},"reply_distribution":{"1":11,"2":4,"3":7,"4":5,"5":91},"review_count":431,"reviews_with_images":49,"reviews_with_text":431,"source_id":3,"source_name":"2Gis & Otello","total_with_reply":118}],"meta":{"generated_at":"2026-07-30T05:33:12.210830Z","group_id":20,"group_ids":[20],"published_from":"2000-01-01","published_to":"2026-07-30","source_ids":[1,2,3,104,105,106,108]},"summary":{"avg_rating":4.52,"rating_distribution":{"1":238,"2":127,"3":346,"4":943,"5":4058},"reply":{"last_reply_date":"2026-07-23","reply_by_rating":{"1":57,"2":33,"3":95,"4":183,"5":905},"total_with_reply":1273,"unanswered_negative":265},"reviews_with_images":688,"reviews_with_text":3825,"total_reviews":5714}}],"example":{"by_source":[{"avg_rating":4.52,"latest_review_date":"2026-07-29","rating_distribution":{"1":147,"2":69,"3":164,"4":457,"5":2458},"reply_distribution":{"1":0,"2":0,"3":0,"4":0,"5":0},"review_count":3295,"reviews_with_images":207,"reviews_with_text":1723,"source_id":2,"source_name":"Google Reviews","total_with_reply":0},{"avg_rating":4.51,"latest_review_date":"2026-07-29","rating_distribution":{"1":50,"2":28,"3":62,"4":113,"5":803},"reply_distribution":{"1":45,"2":24,"3":59,"4":111,"5":770},"review_count":1056,"reviews_with_images":331,"reviews_with_text":1056,"source_id":1,"source_name":"YandexMaps","total_with_reply":1009},{"avg_rating":4.54,"latest_review_date":"2026-07-29","rating_distribution":{"1":5,"2":13,"3":85,"4":293,"5":283},"reply_distribution":{"1":1,"2":5,"3":29,"4":67,"5":44},"review_count":680,"reviews_with_images":58,"reviews_with_text":462,"source_id":106,"source_name":"Ostrovok","total_with_reply":146},{"avg_rating":4.46,"latest_review_date":"2026-07-30","rating_distribution":{"1":29,"2":11,"3":21,"4":43,"5":327},"reply_distribution":{"1":11,"2":4,"3":7,"4":5,"5":91},"review_count":431,"reviews_with_images":49,"reviews_with_text":431,"source_id":3,"source_name":"2Gis & Otello","total_with_reply":118}],"meta":{"generated_at":"2026-07-30T05:33:12.210830Z","group_id":20,"group_ids":[20],"published_from":"2000-01-01","published_to":"2026-07-30","source_ids":[1,2,3,104,105,106,108]},"summary":{"avg_rating":4.52,"rating_distribution":{"1":238,"2":127,"3":346,"4":943,"5":4058},"reply":{"last_reply_date":"2026-07-23","reply_by_rating":{"1":57,"2":33,"3":95,"4":183,"5":905},"total_with_reply":1273,"unanswered_negative":265},"reviews_with_images":688,"reviews_with_text":3825,"total_reviews":5714}}},"AnalyticsSummary":{"properties":{"total_reviews":{"type":"integer","title":"Total Reviews","description":"Всего отзывов за период с учётом фильтров."},"avg_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Rating","description":"Средняя нормализованная оценка (шкала 1–5), округлённая до 2 знаков. `null`, если отзывов нет."},"reviews_with_text":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reviews With Text","description":"Число отзывов с непустым текстом."},"reviews_with_images":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reviews With Images","description":"Число отзывов с изображениями."},"rating_distribution":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Rating Distribution","description":"Распределение отзывов по оценке. Ключи — `1`–`5` (округление оценки вниз до целого), значения — число отзывов. Отзывы без оценки не учитываются."},"reply":{"anyOf":[{"$ref":"#/components/schemas/ReplyStats"},{"type":"null"}],"description":"Статистика ответов организации за период. `null`, если отзывов нет."}},"type":"object","required":["total_reviews"],"title":"AnalyticsSummary","description":"Сводные метрики за период.\n\nЕсли за период нет ни одного отзыва (`total_reviews = 0`), в ответе остаётся\nтолько поле `total_reviews`, остальные поля опускаются."},"BulkLinksResponse":{"properties":{"group_id":{"type":"integer","title":"Group Id","description":"Идентификатор группы, в которую добавлялись ссылки."},"link_results":{"items":{"$ref":"#/components/schemas/LinkResult"},"type":"array","title":"Link Results","description":"Итог по каждой переданной ссылке."}},"type":"object","required":["group_id","link_results"],"title":"BulkLinksResponse","description":"Результат массового добавления ссылок в группу."},"BySourceItem":{"properties":{"source_id":{"type":"integer","title":"Source Id","description":"Идентификатор площадки (см. `GET /sources`)."},"source_name":{"type":"string","title":"Source Name","description":"Отображаемое название площадки."},"review_count":{"type":"integer","title":"Review Count","description":"Число отзывов с этой площадки за период."},"avg_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Rating","description":"Средняя нормализованная оценка (1–5), округлённая до 2 знаков."},"total_with_reply":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total With Reply","description":"Число отзывов с ответом организации."},"reviews_with_text":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reviews With Text","description":"Число отзывов с непустым текстом."},"reviews_with_images":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reviews With Images","description":"Число отзывов с изображениями."},"rating_distribution":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Rating Distribution","description":"Распределение отзывов по оценке (ключи `1`–`5`). Отзывы без оценки не учитываются."},"reply_distribution":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Reply Distribution","description":"Распределение ответов организации по оценке отзыва (ключи `1`–`5`)."},"latest_review_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Latest Review Date","description":"Дата самого свежего отзыва с этой площадки за период."}},"type":"object","required":["source_id","source_name","review_count"],"title":"BySourceItem","description":"Разбивка метрик по одной площадке.\n\nЕсли у площадки нет отзывов за период (`review_count = 0`), в ответе остаются\nтолько `source_id`, `source_name` и `review_count`."},"CollectingInfo":{"properties":{"links_total":{"type":"integer","title":"Links Total","description":"Сколько площадок (ссылок) участвует в выдаче."},"collecting_total":{"type":"integer","title":"Collecting Total","description":"По скольким из них первый сбор идёт прямо сейчас или стоит в очереди. Отзывы появятся сами, повторный запрос через несколько десятков секунд."},"failed_total":{"type":"integer","title":"Failed Total","description":"По скольким из них первый сбор не удался. Состояние каждой площадки — в `scrape_status`/`scrape_error` объекта."},"message":{"type":"string","title":"Message","description":"Готовая формулировка на английском. Для интерфейса на другом языке стройте текст по числам выше."}},"type":"object","required":["links_total","collecting_total","failed_total","message"],"title":"CollectingInfo","description":"Первый сбор по части площадок объекта ещё не состоялся.\n\nПрисутствует ТОЛЬКО пока это правда — как и `access`, само наличие ключа и\nесть флаг. Пропадает навсегда, как только по площадке прошёл успешный сбор,\nдаже если он не принёс ни одного отзыва: «собрали и там пусто» — это ответ,\nа не незавершённость.\n\nОтвечает на единственный вопрос, на который пустая страница ответить не\nможет: «отзывов нет или вы их ещё не собрали»."},"ErrorResponse":{"properties":{"detail":{"type":"string","title":"Detail"},"code":{"type":"string","title":"Code"}},"type":"object","required":["detail","code"],"title":"ErrorResponse","description":"Standard error response returned by all exception handlers."},"FiltersV2":{"properties":{"allowed_ratings":{"items":{"type":"integer"},"type":"array","title":"Allowed Ratings"},"allowed_sources":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Sources"},"has_text":{"type":"boolean","title":"Has Text","default":false},"has_images":{"type":"boolean","title":"Has Images","default":false}},"type":"object","title":"FiltersV2","description":"Фильтры доставки (/v2). AND-комбинируются. Канонические имена\n``has_text`` / ``has_images`` вместо внутренних ``require_text`` /\n``require_images``.","examples":[{"allowed_ratings":[1,2],"allowed_sources":["yandex_maps"],"has_images":false,"has_text":true}],"example":{"allowed_ratings":[1,2],"allowed_sources":["yandex_maps"],"has_images":false,"has_text":true}},"FolderCreateV2":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Название папки (1–255 символов). Пробелы по краям обрезаются; строка из одних пробелов — `422`. Уникальность не требуется: две папки могут называться одинаково."},"parent_folder_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parent Folder Id","description":"Положить папку внутрь другой папки. Не передано или `null` — папка корневая. Родитель должен существовать и принадлежать вам, иначе `404`. Если указанная папка сама лежит внутри другой — `400 folder_depth_exceeded`: глубина ограничена двумя уровнями."},"group_ids":{"anyOf":[{"items":{"type":"integer"},"type":"array","maxItems":200},{"type":"null"}],"title":"Group Ids","description":"Группы, которые сразу положить в папку (до 200). Повторы схлопываются. Чужие, удалённые и несуществующие группы пропускаются: папка создаётся с остальными, а пропущенные идентификаторы возвращаются в `ignored_group_ids`. Одна группа может лежать в скольких угодно папках."}},"type":"object","required":["name"],"title":"FolderCreateV2","description":"Создание папки (/v2).","example":{"group_ids":[20,21],"name":"Сеть «Север»"}},"FolderDeleteResponse":{"properties":{"id":{"type":"integer","title":"Id","description":"Идентификатор удалённой папки."},"name":{"type":"string","title":"Name","description":"Название удалённой папки."},"deleted":{"type":"boolean","title":"Deleted","description":"Признак успешного удаления (`true`)."}},"type":"object","required":["id","name","deleted"],"title":"FolderDeleteResponse","description":"Подтверждение удаления папки.","examples":[{"deleted":true,"id":3,"name":"Сеть «Север»"}],"example":{"deleted":true,"id":3,"name":"Сеть «Север»"}},"FolderGroupV2":{"properties":{"id":{"type":"integer","title":"Id","description":"Идентификатор группы."},"name":{"type":"string","title":"Name","description":"Название группы."}},"type":"object","required":["id","name"],"title":"FolderGroupV2","description":"Группа в составе папки — идентификатор и название.","examples":[{"id":20,"name":"Националь"}],"example":{"id":20,"name":"Националь"}},"FolderUpdateV2":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name","description":"Новое название папки (1–255 символов). Если не передано — не меняется."},"parent_folder_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parent Folder Id","description":"Перенести папку. Передайте идентификатор папки-родителя, чтобы вложить, или `null`, чтобы вынести в корень. Если поле **не передано** — родитель не меняется. Родитель должен существовать и принадлежать вам (`404`). `400 folder_depth_exceeded`, если родитель сам вложен, если у переносимой папки есть свои живые подпапки или если папку делают родителем самой себе."},"group_ids":{"anyOf":[{"items":{"type":"integer"},"type":"array","maxItems":200},{"type":"null"}],"title":"Group Ids","description":"**Полная замена** состава папки, а не добавление: пришедший список становится составом целиком. Очистить состав — прислать `[]`. Не передано **или `null`** — состав не трогаем (`null` намеренно не считается очисткой: иначе сгенерированный клиент, присылающий все поля, стирал бы состав при обычном переименовании). Повторы схлопываются. Чужие, удалённые и несуществующие группы пропускаются: состав становится тем, что из присланного списка удалось положить, а пропущенные идентификаторы возвращаются в `ignored_group_ids`."}},"type":"object","title":"FolderUpdateV2","description":"Изменение папки (/v2). Не переданные поля не меняются.","example":{"group_ids":[20],"name":"Сеть «Север»"}},"FolderV2":{"properties":{"id":{"type":"integer","title":"Id","description":"Идентификатор папки. Обычное целое число — папки не участвуют в кодировании идентификаторов."},"user_id":{"type":"integer","title":"User Id","description":"Идентификатор владельца папки. Он же её создатель: папка всегда создаётся себе — `POST /v2/folders` не принимает чужого владельца."},"name":{"type":"string","title":"Name","description":"Название папки."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Момент создания папки (UTC, ISO 8601)."},"parent_folder_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parent Folder Id","description":"Идентификатор родительской папки. `null` — папка корневая. Глубина ограничена двумя уровнями: папка → подпапка → группы, поэтому у подпапки подпапок быть не может."},"groups":{"items":{"$ref":"#/components/schemas/FolderGroupV2"},"type":"array","title":"Groups","description":"Группы, лежащие непосредственно в этой папке (без групп подпапок). Приходят объектами с названиями, чтобы не ходить за ними в `GET /v2/groups`; на вход `POST`/`PATCH` принимают массив идентификаторов `group_ids`. Здесь виден **состав**, а не то, что попадёт в выдачу: выключенная (`is_active=false`) группа остаётся в составе, но отзывов и аналитики по `folder_id` не даёт — то же правило, что и в списке групп. Удалённая группа из состава пропадает.","default":[]},"subfolders":{"items":{"$ref":"#/components/schemas/FolderV2"},"type":"array","title":"Subfolders","description":"Подпапки этой папки — каждая со своим составом. Порядок тот же, что у папок верхнего уровня: по названию, при совпадении — по `id`. Глубина ограничена двумя уровнями, поэтому у подпапки здесь всегда пустой список. У подпапки есть и свои группы, и они не смешиваются с группами родителя: `groups` каждой папки — только её собственный состав. В выдачу по `folder_id` (отзывы, аналитика) попадают группы папки **и** её подпапок."}},"type":"object","required":["id","user_id","name","created_at"],"title":"FolderV2","description":"Папка со своим составом и вложенными подпапками (/v2).","examples":[{"created_at":"2026-07-28T16:50:24","groups":[{"id":20,"name":"Националь"},{"id":21,"name":"Метрополь"}],"id":3,"name":"Сеть «Север»","subfolders":[{"created_at":"2026-07-28T17:02:11","groups":[{"id":22,"name":"Балчуг"}],"id":7,"name":"Москва","parent_folder_id":3,"subfolders":[],"user_id":6}],"user_id":6}],"example":{"created_at":"2026-07-28T16:50:24","groups":[{"id":20,"name":"Националь"},{"id":21,"name":"Метрополь"}],"id":3,"name":"Сеть «Север»","subfolders":[{"created_at":"2026-07-28T17:02:11","groups":[{"id":22,"name":"Балчуг"}],"id":7,"name":"Москва","parent_folder_id":3,"subfolders":[],"user_id":6}],"user_id":6}},"FolderWriteV2":{"properties":{"id":{"type":"integer","title":"Id","description":"Идентификатор папки. Обычное целое число — папки не участвуют в кодировании идентификаторов."},"user_id":{"type":"integer","title":"User Id","description":"Идентификатор владельца папки. Он же её создатель: папка всегда создаётся себе — `POST /v2/folders` не принимает чужого владельца."},"name":{"type":"string","title":"Name","description":"Название папки."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Момент создания папки (UTC, ISO 8601)."},"parent_folder_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parent Folder Id","description":"Идентификатор родительской папки. `null` — папка корневая. Глубина ограничена двумя уровнями: папка → подпапка → группы, поэтому у подпапки подпапок быть не может."},"groups":{"items":{"$ref":"#/components/schemas/FolderGroupV2"},"type":"array","title":"Groups","description":"Группы, лежащие непосредственно в этой папке (без групп подпапок). Приходят объектами с названиями, чтобы не ходить за ними в `GET /v2/groups`; на вход `POST`/`PATCH` принимают массив идентификаторов `group_ids`. Здесь виден **состав**, а не то, что попадёт в выдачу: выключенная (`is_active=false`) группа остаётся в составе, но отзывов и аналитики по `folder_id` не даёт — то же правило, что и в списке групп. Удалённая группа из состава пропадает.","default":[]},"subfolders":{"items":{"$ref":"#/components/schemas/FolderV2"},"type":"array","title":"Subfolders","description":"Подпапки этой папки — каждая со своим составом. Порядок тот же, что у папок верхнего уровня: по названию, при совпадении — по `id`. Глубина ограничена двумя уровнями, поэтому у подпапки здесь всегда пустой список. У подпапки есть и свои группы, и они не смешиваются с группами родителя: `groups` каждой папки — только её собственный состав. В выдачу по `folder_id` (отзывы, аналитика) попадают группы папки **и** её подпапок."},"ignored_group_ids":{"items":{"type":"integer"},"type":"array","title":"Ignored Group Ids","description":"Идентификаторы из присланного `group_ids`, которые в состав не попали: чужие, удалённые или несуществующие группы. Запрос при этом выполняется — остальные группы кладутся в папку. Непустой список — обычно опечатка в идентификаторе на стороне клиента: ошибки не будет, но и группы в папке не будет тоже. При чтении папки поля нет."}},"type":"object","required":["id","user_id","name","created_at"],"title":"FolderWriteV2","description":"Ответ на запись папки: сама папка плюс отчёт о пропущенных группах.\n\nОтдельная модель, а не поле на :class:`FolderV2`: `ignored_group_ids` —\nфакт одного конкретного запроса на запись, а не свойство папки, и в чтении\n(список, `GET /v2/folders/{id}`, подпапки) ему делать нечего.","examples":[{"created_at":"2026-07-28T16:50:24","groups":[{"id":20,"name":"Националь"}],"id":3,"ignored_group_ids":[999],"name":"Сеть «Север»","subfolders":[],"user_id":6}],"example":{"created_at":"2026-07-28T16:50:24","groups":[{"id":20,"name":"Националь"}],"id":3,"ignored_group_ids":[999],"name":"Сеть «Север»","subfolders":[],"user_id":6}},"GroupCreateResponseV2":{"properties":{"id":{"type":"integer","title":"Id","description":"Идентификатор созданной группы."},"name":{"type":"string","title":"Name","description":"Название группы."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Момент создания группы (UTC, ISO 8601)."},"link_results":{"anyOf":[{"items":{"$ref":"#/components/schemas/LinkResult"},"type":"array"},{"type":"null"}],"title":"Link Results","description":"Итог по каждой переданной ссылке (успех с `link_id` или `error`). `null`, если ссылки при создании не передавались."}},"type":"object","required":["id","name","created_at"],"title":"GroupCreateResponseV2","description":"Созданная группа с результатом по каждой ссылке.\n\n``user_id`` здесь нет: владелец — это всегда владелец ключа, которым сделан\nзапрос, так что поле ничего не сообщает вызывающему.","examples":[{"created_at":"2026-08-03T13:41:08","id":218,"link_results":[{"link_id":521,"link_name":"Яндекс Карты","source_name":"yandex_maps","url":"https://yandex.com/maps/org/national/1020542995"}],"name":"Гостиница «Националь»"}],"example":{"created_at":"2026-08-03T13:41:08","id":218,"link_results":[{"link_id":521,"link_name":"Яндекс Карты","source_name":"yandex_maps","url":"https://yandex.com/maps/org/national/1020542995"}],"name":"Гостиница «Националь»"}},"GroupCreateV2":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Название группы — объекта учёта отзывов. Отображается в интерфейсе; должно быть непустым (1–255 символов)."},"links":{"anyOf":[{"items":{"$ref":"#/components/schemas/LinkInputV2"},"type":"array","maxItems":200},{"type":"null"}],"title":"Links","description":"Ссылки на объект на разных площадках, добавляемые сразу при создании (не больше 200 за раз). Можно не передавать и добавить их позже через `POST /v2/groups/{group_id}/links`."}},"type":"object","required":["name"],"title":"GroupCreateV2","description":"Создание группы с необязательным набором ссылок (/v2).\n\nВ одной группе может быть не больше одной ссылки на каждую площадку.","example":{"links":[{"is_active":true,"link_name":"Yandex Travel — Астория","url":"https://travel.yandex.ru/hotels/saint-petersburg/astoria/"},{"is_active":true,"url":"https://yandex.ru/maps/org/astoria/1234567890/"}],"name":"Астория"}},"GroupDeleteResponse":{"properties":{"id":{"type":"integer","title":"Id","description":"Идентификатор удалённой группы."},"name":{"type":"string","title":"Name","description":"Название удалённой группы."},"deleted":{"type":"boolean","title":"Deleted","description":"Признак успешного удаления (`true`)."}},"type":"object","required":["id","name","deleted"],"title":"GroupDeleteResponse","description":"Подтверждение удаления группы."},"GroupScrapeStatus":{"type":"string","enum":["pending","in_progress","success","partial","failed"],"title":"GroupScrapeStatus","description":"Сводный статус сбора по группе — свёртка статусов её включённых ссылок."},"GroupUpdateV2":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name","description":"Новое название группы (1–255 символов). Если не передано — не меняется."},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Включить (`true`) или выключить (`false`) группу. Выключенная группа не отдаёт отзывы, аналитику, скрытые и закреплённые и не собирается, но остаётся в списке, редактируется, удаляется и продолжает занимать место в лимите объектов — переключение на лимит не влияет, место освобождает только удаление группы. Если не передано — не меняется. Каждое переключение пишется в историю группы."}},"type":"object","title":"GroupUpdateV2","description":"Изменение полей группы. Не переданные поля не меняются.","example":{"is_active":false,"name":"Астория"}},"GroupV2":{"properties":{"id":{"type":"integer","title":"Id","description":"Идентификатор группы."},"name":{"type":"string","title":"Name","description":"Название группы."},"is_active":{"type":"boolean","title":"Is Active","description":"Статус группы. `false` — владелец её выключил: отзывы и аналитика по ней не отдаются (`GET /v2/reviews` и `GET /v2/analytics` вернут 404), скрытые/закреплённые тоже, ссылки не собираются. Группа при этом остаётся: её видно в списке, можно переименовать, править ссылки и удалить, и она продолжает занимать место в лимите объектов. Переключается через `PUT /v2/groups/{group_id}`, а сразу у нескольких групп — через `PUT /v2/groups`.","default":true},"scrape_status":{"$ref":"#/components/schemas/GroupScrapeStatus","description":"Сводный статус сбора по группе — свёртка `scrape_status` её включённых ссылок: `in_progress` — хотя бы по одной сбор идёт сейчас; `failed` — упали все; `partial` — упала часть; `success` — собрались все; `pending` — собирать нечего или что-то ещё ни разу не собиралось. Выключенные ссылки (`is_active=false`) не учитываются. Только чтение.","default":"pending"},"last_scraped_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Scraped At","description":"Момент последнего сбора по группе — самый поздний `last_scraped_at` среди включённых ссылок (UTC, ISO 8601). `null`, если ни одна из них ещё не собиралась. Только чтение."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Момент создания группы (UTC, ISO 8601)."},"links":{"items":{"$ref":"#/components/schemas/LinkV2"},"type":"array","title":"Links","description":"Ссылки группы на объект на разных площадках.","default":[]}},"type":"object","required":["id","name","created_at"],"title":"GroupV2","description":"Группа со вложенными ссылками (/v2).","examples":[{"created_at":"2026-02-23T17:29:38","id":20,"is_active":true,"last_scraped_at":"2026-07-30T05:13:49","links":[{"auto_name":"Яндекс.Карты: Националь","created_at":"2026-02-23T17:30:52","id":177,"is_active":true,"last_scraped_at":"2026-07-30T05:13:48","last_scraped_review_date":"2026-07-29T18:36:26","link_name":"Яндекс Националь","scrape_status":"success","source_id":1,"url":"https://yandex.ru/maps/org/national/1020542995"},{"auto_name":"Ostrovok: Гостиница Националь Москва","created_at":"2026-02-23T17:32:06","id":178,"is_active":true,"last_scraped_at":"2026-07-30T05:13:49","last_scraped_review_date":"2026-07-29T15:24:34","link_name":"Островок Националь","scrape_status":"success","source_id":106,"url":"https://ostrovok.ru/hotel/russia/moscow/mid7467340/gostinitsa_natsional_moskva"}],"name":"Националь","scrape_status":"success"}],"example":{"created_at":"2026-02-23T17:29:38","id":20,"is_active":true,"last_scraped_at":"2026-07-30T05:13:49","links":[{"auto_name":"Яндекс.Карты: Националь","created_at":"2026-02-23T17:30:52","id":177,"is_active":true,"last_scraped_at":"2026-07-30T05:13:48","last_scraped_review_date":"2026-07-29T18:36:26","link_name":"Яндекс Националь","scrape_status":"success","source_id":1,"url":"https://yandex.ru/maps/org/national/1020542995"},{"auto_name":"Ostrovok: Гостиница Националь Москва","created_at":"2026-02-23T17:32:06","id":178,"is_active":true,"last_scraped_at":"2026-07-30T05:13:49","last_scraped_review_date":"2026-07-29T15:24:34","link_name":"Островок Националь","scrape_status":"success","source_id":106,"url":"https://ostrovok.ru/hotel/russia/moscow/mid7467340/gostinitsa_natsional_moskva"}],"name":"Националь","scrape_status":"success"}},"GroupsSwitchV2":{"properties":{"group_ids":{"items":{"type":"integer"},"type":"array","maxItems":200,"minItems":1,"title":"Group Ids","description":"Идентификаторы групп (1–200 за запрос). Повторы схлопываются. Id, недоступный вызывающему (чужой, несуществующий или удалённый), молча пропускается — остальные группы набора переключаются. В ответе такой id просто отсутствует; если не нашлось ни одной группы, `data` пустой."},"is_active":{"type":"boolean","title":"Is Active","description":"Куда переключить все перечисленные группы: `true` — включить, `false` — выключить. Группы, которые уже в этом состоянии, пропускаются молча (в историю не пишутся). На лимит объектов переключение не влияет."}},"type":"object","required":["group_ids","is_active"],"title":"GroupsSwitchV2","description":"Массовое переключение выключателя групп (/v2).\n\nТо же поле ``is_active``, что и в ``GroupUpdateV2``, применённое к набору\nгрупп за один вызов. Одно значение на запрос: «включить эти» или «выключить\nэти»; разнородный набор — это два вызова. Переключается всё, что доступно\nвызывающему; недоступные id пропускаются.","example":{"group_ids":[12,13,27],"is_active":false}},"HiddenReviewsMutationResponse":{"properties":{"detail":{"type":"string","title":"Detail"},"hidden_count":{"type":"integer","title":"Hidden Count"}},"type":"object","required":["detail","hidden_count"],"title":"HiddenReviewsMutationResponse","description":"Result of a hide/unhide batch mutation."},"HiddenReviewsRequest":{"properties":{"review_ids":{"items":{"type":"integer"},"type":"array","maxItems":200,"minItems":1,"title":"Review Ids"}},"type":"object","required":["review_ids"],"title":"HiddenReviewsRequest","description":"Список id отзывов, которые нужно скрыть или вернуть из скрытых (до 200 за запрос).","example":{"review_ids":[12345,67890,54321]}},"LinkInputV2":{"properties":{"url":{"type":"string","maxLength":2083,"minLength":1,"format":"uri","title":"Url","description":"Полный URL страницы объекта на площадке. Площадка определяется по домену автоматически; домен должен совпадать с одной из площадок из `GET /v2/sources`."},"is_active":{"type":"boolean","title":"Is Active","description":"Собирать ли отзывы по этой ссылке. `true` — ссылка попадает в автосбор; `false` — добавлена, но не опрашивается. По умолчанию `true`.","default":true},"link_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Link Name","description":"Своё название ссылки внутри группы (например, «Филиал на Невском»). Если не задано, используется название, полученное с площадки."}},"type":"object","required":["url"],"title":"LinkInputV2","description":"Ссылка на объект на одной из площадок для добавления в группу.","example":{"is_active":true,"link_name":"Яндекс Националь","url":"https://yandex.ru/maps/org/national/1020542995"}},"LinkRemoveResponse":{"properties":{"detail":{"type":"string","title":"Detail","description":"Человекочитаемое сообщение об итоге операции."},"group_id":{"type":"integer","title":"Group Id","description":"Идентификатор группы, из которой удалена ссылка."},"link_id":{"type":"integer","title":"Link Id","description":"Идентификатор удалённой ссылки."}},"type":"object","required":["detail","group_id","link_id"],"title":"LinkRemoveResponse","description":"Подтверждение удаления ссылки из группы."},"LinkResult":{"properties":{"url":{"type":"string","title":"Url","description":"URL ссылки, к которой относится результат."},"link_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Link Id","description":"Идентификатор созданной ссылки. Присутствует при успехе; `null` при ошибке."},"source_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Name","description":"Машинное имя площадки, определённой по URL. `null`, если определить не удалось."},"link_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Link Name","description":"Своё название ссылки внутри группы, если было задано."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Причина, по которой ссылку не удалось добавить. Присутствует только при ошибке (например, неизвестный домен или дубль в группе)."},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Code","description":"Машинная причина отказа — по ней и следует ветвиться: `source_not_detected` (площадка по URL не определилась), `source_url_invalid` (площадка определена, но ссылка ей не подходит), `source_requires_upgrade` (площадка вне вашего тарифа), `source_already_linked` (эта площадка в объекте уже есть — она может быть только одна), `link_already_added` (ровно эта ссылка уже добавлена). Присутствует только при ошибке."}},"type":"object","required":["url"],"title":"LinkResult","description":"Результат добавления одной ссылки (в ответе на создание группы / массовое добавление)."},"LinkUpdate":{"properties":{"link_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Link Name","description":"Новое своё название ссылки внутри группы. Не передано — не меняется."},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Включить (`true`) или выключить (`false`) сбор по ссылке. Не передано — не меняется."}},"type":"object","title":"LinkUpdate","description":"Изменение параметров ссылки в группе. Передавайте только изменяемые поля.","example":{"is_active":true,"link_name":"Яндекс Националь (основной вход)"}},"LinkV2":{"properties":{"id":{"type":"integer","title":"Id","description":"Идентификатор ссылки."},"url":{"type":"string","title":"Url","description":"URL страницы объекта на площадке."},"source_id":{"type":"integer","title":"Source Id","description":"Идентификатор площадки ссылки (см. `GET /v2/sources`)."},"link_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Link Name","description":"Своё название ссылки внутри группы, если задано."},"auto_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Auto Name","description":"Название, определённое нами по ссылке, с подписью площадки через двоеточие: `2ГИС: Кофейня Север`. Пока имя не прочитано с площадки, здесь одна подпись — `2ГИС`. Показывать: `link_name`, если задано, иначе `auto_name`."},"is_active":{"type":"boolean","title":"Is Active","description":"Включена ли ссылка в автосбор отзывов."},"scrape_status":{"$ref":"#/components/schemas/ScrapeStatus","description":"Статус последнего сбора: `pending` / `in_progress` / `success` / `failed`."},"scrape_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scrape Error","description":"Текст ошибки последнего неудачного сбора. `null`, когда сбор успешен."},"last_scraped_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Scraped At","description":"Момент последнего сбора по ссылке (UTC, ISO 8601)."},"last_scraped_review_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Scraped Review Date","description":"Дата публикации самого свежего собранного отзыва по ссылке."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Момент добавления ссылки в группу (UTC, ISO 8601)."}},"type":"object","required":["id","url","source_id","is_active","scrape_status","created_at"],"title":"LinkV2","description":"Ссылка (объект на площадке) со статусом сбора (/v2).","examples":[{"auto_name":"Яндекс.Карты: Националь","created_at":"2026-02-23T17:30:52","id":177,"is_active":true,"last_scraped_at":"2026-07-30T05:13:48","last_scraped_review_date":"2026-07-29T18:36:26","link_name":"Яндекс Националь","scrape_status":"success","source_id":1,"url":"https://yandex.ru/maps/org/national/1020542995"}],"example":{"auto_name":"Яндекс.Карты: Националь","created_at":"2026-02-23T17:30:52","id":177,"is_active":true,"last_scraped_at":"2026-07-30T05:13:48","last_scraped_review_date":"2026-07-29T18:36:26","link_name":"Яндекс Националь","scrape_status":"success","source_id":1,"url":"https://yandex.ru/maps/org/national/1020542995"}},"Page_GroupV2_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/GroupV2"},"type":"array","title":"Data","description":"Элементы текущей страницы."},"total":{"type":"integer","title":"Total","description":"Общее число элементов, подходящих под фильтры (без учёта limit/offset)."},"limit":{"type":"integer","title":"Limit","description":"Размер страницы, применённый к запросу."},"offset":{"type":"integer","title":"Offset","description":"Смещение от начала выборки, применённое к запросу."},"has_next":{"type":"boolean","title":"Has Next","description":"Есть ли ещё элементы за пределами текущей страницы."}},"type":"object","required":["data","total","limit","offset","has_next"],"title":"Page[GroupV2]"},"Page_ReviewV2_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ReviewV2"},"type":"array","title":"Data","description":"Элементы текущей страницы."},"total":{"type":"integer","title":"Total","description":"Общее число элементов, подходящих под фильтры (без учёта limit/offset)."},"limit":{"type":"integer","title":"Limit","description":"Размер страницы, применённый к запросу."},"offset":{"type":"integer","title":"Offset","description":"Смещение от начала выборки, применённое к запросу."},"has_next":{"type":"boolean","title":"Has Next","description":"Есть ли ещё элементы за пределами текущей страницы."}},"type":"object","required":["data","total","limit","offset","has_next"],"title":"Page[ReviewV2]"},"PinnedAddRequest":{"properties":{"review_ids":{"items":{"type":"integer"},"type":"array","maxItems":30,"minItems":1,"title":"Review Ids"}},"type":"object","required":["review_ids"],"title":"PinnedAddRequest","description":"Список id отзывов: добавить в конец закреплённых или открепить (до 30 за запрос).","example":{"review_ids":[12345,67890]}},"PinnedReorderRequest":{"properties":{"ordered_review_ids":{"items":{"type":"integer"},"type":"array","maxItems":30,"title":"Ordered Review Ids"}},"type":"object","required":["ordered_review_ids"],"title":"PinnedReorderRequest","description":"Полный список закреплённых отзывов в нужном порядке показа (до 30, без повторов).\n\nПустой список открепляет все.","example":{"ordered_review_ids":[67890,12345,54321]}},"ReplyStats":{"properties":{"total_with_reply":{"type":"integer","title":"Total With Reply","description":"Число отзывов, на которые есть ответ организации."},"unanswered_negative":{"type":"integer","title":"Unanswered Negative","description":"Число негативных отзывов (оценка строго ниже 3) без ответа организации."},"reply_by_rating":{"additionalProperties":{"type":"integer"},"type":"object","title":"Reply By Rating","description":"Распределение ответов организации по оценке отзыва. Ключи — `1`–`5` (округление оценки вниз до целого), значения — число ответов на отзывы с такой оценкой."},"last_reply_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Last Reply Date","description":"Дата последнего ответа организации за период. `null`, если ответов нет."}},"type":"object","required":["total_with_reply","unanswered_negative","reply_by_rating"],"title":"ReplyStats","description":"Reply statistics within summary block."},"ReplyV2":{"properties":{"text":{"type":"string","title":"Text","description":"Текст ответа, который должен стоять на площадке."},"status":{"type":"string","enum":["pending","publishing","published","failed","deleted"],"title":"Status","description":"Состояние **вашего ответа**, а не отзыва. `pending` — ждёт публикации, `publishing` — запрос к площадке в полёте, `published` — площадка приняла, `deleted` — ответ снят, `failed` — опубликовать не удалось."},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Когда запись менялась в последний раз."}},"type":"object","required":["text","status","updated_at"],"title":"ReplyV2","description":"Что мы приняли и в каком оно состоянии.\n\nРовно три поля: больше в момент записи и не бывает. `PUT` и `DELETE`\nобнуляют отметки предыдущего намерения, поэтому `published_at` и `error`\nздесь всегда были бы `null` — а поле, пустое в единственный момент, когда\nобъект отдаётся, это обещание, а не контракт.","examples":[{"status":"pending","text":"Спасибо за отзыв!","updated_at":"2026-08-16T09:12:44"}],"example":{"status":"pending","text":"Спасибо за отзыв!","updated_at":"2026-08-16T09:12:44"}},"ReplyWriteV2":{"properties":{"text":{"type":"string","minLength":1,"title":"Text","description":"Текст ответа. Пустой текст не принимается — чтобы удалить ответ, используйте `DELETE`."}},"type":"object","required":["text"],"title":"ReplyWriteV2","description":"Тело `PUT /v2/reviews/{review_id}/reply`.","examples":[{"text":"Спасибо за отзыв! Рады, что всё понравилось."}],"example":{"text":"Спасибо за отзыв! Рады, что всё понравилось."}},"ReturnAll_FolderV2_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/FolderV2"},"type":"array","title":"Data","description":"Все элементы (пагинации нет)."}},"type":"object","required":["data"],"title":"ReturnAll[FolderV2]"},"ReturnAll_GroupV2_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/GroupV2"},"type":"array","title":"Data","description":"Все элементы (пагинации нет)."}},"type":"object","required":["data"],"title":"ReturnAll[GroupV2]"},"ReturnAll_LinkV2_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/LinkV2"},"type":"array","title":"Data","description":"Все элементы (пагинации нет)."}},"type":"object","required":["data"],"title":"ReturnAll[LinkV2]"},"ReturnAll_ReviewV2_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ReviewV2"},"type":"array","title":"Data","description":"Все элементы (пагинации нет)."}},"type":"object","required":["data"],"title":"ReturnAll[ReviewV2]"},"ReturnAll_SourceV2_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/SourceV2"},"type":"array","title":"Data","description":"Все элементы (пагинации нет)."}},"type":"object","required":["data"],"title":"ReturnAll[SourceV2]"},"ReturnAll_WebhookV2_":{"properties":{"data":{"items":{"$ref":"#/components/schemas/WebhookV2"},"type":"array","title":"Data","description":"Все элементы (пагинации нет)."}},"type":"object","required":["data"],"title":"ReturnAll[WebhookV2]"},"ReviewImageV2":{"properties":{"template_url":{"type":"string","title":"Template Url","description":"URL изображения на CDN площадки в исходном виде. У части площадок содержит шаблон размера (`{size}` или `{width}`/`{height}`) и потому не предназначен для прямой вставки — для показа берите `preview` или `image`."},"preview":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preview","description":"Готовый URL уменьшенной версии (превью) для списков и сеток."},"image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image","description":"Готовый URL полноразмерной версии для просмотра."}},"type":"object","required":["template_url"],"title":"ReviewImageV2","description":"Изображение, приложенное к отзыву.\n\n``template_url`` — ссылка площадки «как есть», и у части CDN (Яндекс,\nОстровок, TripAdvisor, 2ГИС) она содержит шаблон размера, то есть сама по\nсебе не открывается. Готовые к показу ссылки — ``preview`` и ``image``.\n\nПравила подстановки размеров держим в паре с ``ReviewImageResponse``\n(app/schemas/review.py) — при правке менять обе.","examples":[{"image":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/XXXL","preview":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/M","template_url":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/{size}"}],"example":{"image":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/XXXL","preview":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/M","template_url":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/{size}"}},"ReviewV2":{"properties":{"id":{"type":"integer","title":"Id","description":"Идентификатор отзыва в сервисе."},"link_id":{"type":"integer","title":"Link Id","description":"Идентификатор ссылки (объекта на площадке)."},"source_id":{"type":"integer","title":"Source Id","description":"Идентификатор площадки, с которой собран отзыв."},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author","description":"Имя автора; `null`, если анонимен."},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text","description":"Текст отзыва; `null`/отсутствует, если отзыв без текста."},"review_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Review Date","description":"Дата и время публикации отзыва в том виде, как их показывает площадка — местное время площадки, **не UTC** (единственное исключение; остальные даты в UTC). ISO 8601 без таймзоны."},"rating_original":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rating Original","description":"Исходная оценка в шкале площадки."},"rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rating","description":"Оценка, приведённая к единой шкале 1–5 (дробное). `null`, если оценки нет."},"review_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Review Url","description":"Прямая ссылка на отзыв на площадке. `null`, если недоступна."},"reply_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reply Text","description":"Текст официального ответа организации. `null`, если ответа нет."},"reply_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Reply Date","description":"Дата и время ответа организации (UTC, ISO 8601)."},"has_images":{"type":"boolean","title":"Has Images","description":"Признак наличия изображений в отзыве."},"images":{"items":{"$ref":"#/components/schemas/ReviewImageV2"},"type":"array","title":"Images","description":"Список изображений отзыва.","default":[]},"has_videos":{"type":"boolean","title":"Has Videos","description":"Признак наличия видео в отзыве.","default":false},"videos":{"items":{"$ref":"#/components/schemas/ReviewVideoResponse"},"type":"array","title":"Videos","description":"Список видеовложений отзыва.","default":[]},"is_deleted":{"type":"boolean","title":"Is Deleted","description":"Отзыв обнаружен удалённым на площадке (присутствует только когда `true`).","default":false},"deleted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Deleted At","description":"Момент обнаружения удаления отзыва (UTC, ISO 8601)."},"is_hidden":{"type":"boolean","title":"Is Hidden","description":"Отзыв скрыт в рамках группы (присутствует только когда `true`).","default":false},"is_pinned":{"type":"boolean","title":"Is Pinned","description":"Отзыв закреплён в рамках группы (присутствует только когда `true`).","default":false},"pin_position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pin Position","description":"Позиция закреплённого отзыва: меньше — выше."},"positive":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Positive","description":"Блок «достоинства» (только при `separate_pos_neg=true`)."},"negative":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Negative","description":"Блок «недостатки» (только при `separate_pos_neg=true`)."}},"type":"object","required":["id","link_id","source_id","has_images"],"title":"ReviewV2","description":"Нормализованный отзыв (/v2). Канонические имена полей: ``author`` / ``text`` / ``rating``.","examples":[{"author":"Сергей","has_images":true,"has_videos":false,"id":143046,"images":[{"image":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/XXXL","preview":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/M","template_url":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/{size}"}],"link_id":177,"rating":5.0,"rating_original":"5","reply_date":"2026-07-23T13:27:07","reply_text":"Уважаемый Гость,\nБлагодарим Вас за визит в гостиницу «Националь» и высокую оценку нашего обслуживания. Ждем Вас снова!\nС наилучшими пожеланиями,\t\nЕлена Позолотина\nДиректор по операционной деятельности.\n","review_date":"2026-07-21T18:07:03","review_url":"https://yandex.ru/maps/org/1020542995/reviews?reviews[publicId]=u182gzp1r9rg4urew906ky9dd0","source_id":1,"text":"Замечательно. Приеду ещё. ","videos":[]}],"example":{"author":"Сергей","has_images":true,"has_videos":false,"id":143046,"images":[{"image":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/XXXL","preview":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/M","template_url":"https://avatars.mds.yandex.net/get-altay/17743308/2a0000019f85dc34d7ee3b24b4b9ec82bdbd/{size}"}],"link_id":177,"rating":5.0,"rating_original":"5","reply_date":"2026-07-23T13:27:07","reply_text":"Уважаемый Гость,\nБлагодарим Вас за визит в гостиницу «Националь» и высокую оценку нашего обслуживания. Ждем Вас снова!\nС наилучшими пожеланиями,\t\nЕлена Позолотина\nДиректор по операционной деятельности.\n","review_date":"2026-07-21T18:07:03","review_url":"https://yandex.ru/maps/org/1020542995/reviews?reviews[publicId]=u182gzp1r9rg4urew906ky9dd0","source_id":1,"text":"Замечательно. Приеду ещё. ","videos":[]}},"ReviewVideoResponse":{"properties":{"kind":{"type":"string","enum":["embed","file","hls"],"title":"Kind","description":"Тип вложения: `embed` — встраиваемый плеер площадки (открывать в sandbox-iframe), `file` — прямая ссылка на видеофайл, `hls` — HLS-поток (`.m3u8`)."},"video_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Url","description":"URL видео или встраиваемого плеера в зависимости от `kind`."},"thumbnail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thumbnail","description":"URL кадра-заставки (превью) видео."},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Width","description":"Ширина видео в пикселях, если известна."},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Height","description":"Высота видео в пикселях, если известна."},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms","description":"Длительность видео в миллисекундах, если известна."}},"type":"object","required":["kind"],"title":"ReviewVideoResponse","description":"Schema for a review video attachment (links only, kind-discriminated).\n\n`video_url` is a plain `str` (mirrors ReviewImageResponse.image_url), NOT\nHttpUrl: an invalid URL from the DB must not raise ValidationError and 500\nthe whole review batch. Safety is enforced on the way IN (host-allowlist\nin the scraper + scheme-guard in the save path), and on the frontend, which\nonly embeds allowlisted hosts in a sandboxed iframe (see plan §8.5)."},"ReviewsPageV2":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ReviewV2"},"type":"array","title":"Data","description":"Элементы текущей страницы."},"total":{"type":"integer","title":"Total","description":"Общее число элементов, подходящих под фильтры (без учёта limit/offset)."},"limit":{"type":"integer","title":"Limit","description":"Размер страницы, применённый к запросу."},"offset":{"type":"integer","title":"Offset","description":"Смещение от начала выборки, применённое к запросу."},"has_next":{"type":"boolean","title":"Has Next","description":"Есть ли ещё элементы за пределами текущей страницы."},"access":{"anyOf":[{"$ref":"#/components/schemas/TrialAccessInfo"},{"type":"null"}],"description":"Ограничение выдачи. Присутствует только когда пробному доступу показали не всё — у обычного клиента ключа нет."},"collecting":{"anyOf":[{"$ref":"#/components/schemas/CollectingInfo"},{"type":"null"}],"description":"Первого сбора по части площадок ещё не было. Присутствует только до первого успешного сбора — дальше пустая страница значит «отзывов нет», и ключа в ответе нет."}},"type":"object","required":["data","total","limit","offset","has_next"],"title":"ReviewsPageV2","description":"Страница отзывов /v2 — конверт `Page` плюс сведения об ограничении.\n\nОтдельный подкласс, а не поле в общем `Page`: `access` бывает только у\nотзывов, и в схеме групп или источников оно означало бы обещание, которого\nте ручки не дают."},"ReviewsSyncPageV2":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ReviewV2"},"type":"array","title":"Data","description":"Элементы текущей страницы."},"next_cursor":{"type":"string","title":"Next Cursor","description":"Курсор для следующего запроса. Приходит всегда, в том числе на последней и на пустой странице — его и нужно сохранить у себя до следующего прогона."},"has_next":{"type":"boolean","title":"Has Next","description":"Есть ли ещё элементы прямо сейчас. `false` — обход догнал текущее состояние; возвращайтесь с сохранённым курсором позже."},"access":{"anyOf":[{"$ref":"#/components/schemas/TrialAccessInfo"},{"type":"null"}],"description":"Ограничение выдачи. Присутствует только когда пробному доступу отдали не всё — у обычного клиента ключа нет."}},"type":"object","required":["data","next_cursor","has_next"],"title":"ReviewsSyncPageV2","description":"Страница потока изменений отзывов."},"ScrapeStatus":{"type":"string","enum":["pending","in_progress","success","failed"],"title":"ScrapeStatus","description":"Статус последнего сбора отзывов по ссылке.\n\nЗначения совпадают с DB-enum `Link.scrape_status` (`app/models/link.py`)."},"SourceV2":{"properties":{"id":{"type":"integer","title":"Id","description":"Числовой идентификатор площадки. Используется в фильтрах `source_ids` эндпоинтов `/reviews` и `/analytics`."},"name":{"type":"string","title":"Name","description":"Машинное имя площадки (латиница, snake_case), стабильно и не меняется. Пример: `yandex_travel`, `google`, `2gis`."},"display_name":{"type":"string","title":"Display Name","description":"Человекочитаемое название площадки для интерфейса. Пример: `YandexTravel`."},"base_url":{"type":"string","title":"Base Url","description":"Базовый адрес площадки. Ссылки, добавляемые в группу, должны вести на страницу объекта именно на этом домене."},"rating_scale":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating Scale","description":"Исходная шкала оценок площадки (например, 5 или 10). Оценки в `/reviews` уже нормализованы к шкале 1–5 (`rating`); исходное значение доступно в `rating_original`. `null`, если площадка не выставляет оценок."},"has_reply":{"type":"boolean","title":"Has Reply","description":"Площадка поддерживает публичные ответы организации на отзывы (в отзывах может быть заполнено поле `reply_text`)."},"has_images":{"type":"boolean","title":"Has Images","description":"Отзывы на площадке могут содержать изображения (поле `images`)."},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Момент добавления площадки в сервис (UTC, ISO 8601, без таймзоны)."},"can_publish_reply":{"type":"boolean","title":"Can Publish Reply","description":"Rewio умеет публиковать ответы на этой площадке. Отличается от `has_reply`: тот говорит, что у отзывов площадки бывают ответы организации, а этот — что ответ можете опубликовать вы, через нас."}},"type":"object","required":["id","name","display_name","base_url","has_reply","has_images","created_at","can_publish_reply"],"title":"SourceV2","description":"Площадка отзывов (/v2).","examples":[{"base_url":"https://yandex.ru/maps/","created_at":"2026-02-08T18:33:17","display_name":"YandexMaps","has_images":true,"has_reply":true,"id":1,"name":"yandex_maps","rating_scale":5}],"example":{"base_url":"https://yandex.ru/maps/","created_at":"2026-02-08T18:33:17","display_name":"YandexMaps","has_images":true,"has_reply":true,"id":1,"name":"yandex_maps","rating_scale":5}},"SupportAccepted":{"properties":{"status":{"type":"string","title":"Status","description":"Всегда `accepted`: обращение принято и отправлено.","examples":["accepted"]}},"type":"object","required":["status"],"title":"SupportAccepted"},"SupportMessage":{"properties":{"subject":{"type":"string","maxLength":200,"minLength":2,"title":"Subject","description":"Тема обращения."},"message":{"type":"string","maxLength":5000,"minLength":5,"title":"Message","description":"Текст обращения."},"contact":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Contact","description":"Как с вами связаться, если ответ на почту аккаунта вам не подходит: телефон, Telegram, другой адрес. Необязательно."}},"type":"object","required":["subject","message"],"title":"SupportMessage"},"TestSubscriptionResponse":{"properties":{"status":{"type":"string","const":"queued","title":"Status"},"test_id":{"type":"string","title":"Test Id"}},"type":"object","required":["status","test_id"],"title":"TestSubscriptionResponse"},"TimeseriesMetaV2":{"properties":{"group_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Group Id","description":"Идентификатор группы, по которой посчитаны данные. В папочном режиме — `null`. Поле **всегда присутствует** в ответе: клиенты, сгенерированные из OpenAPI, ломаются на исчезающих полях."},"folder_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Folder Id","description":"Идентификатор папки, если запрос был по папке. Иначе `null`."},"group_ids":{"items":{"type":"integer"},"type":"array","title":"Group Ids","description":"Фактический состав, по которому посчитаны данные: в групповом режиме — одна группа, в папочном — все группы папки и её подпапок, участвовавшие в расчёте (выключенные и удалённые сюда не попадают).","default":[]},"published_from":{"type":"string","format":"date","title":"Published From","description":"Фактически применённое начало периода по дате публикации, включительно."},"published_to":{"type":"string","format":"date","title":"Published To","description":"Фактически применённый конец периода по дате публикации, включительно."},"source_ids":{"items":{"type":"integer"},"type":"array","title":"Source Ids","description":"Фактически применённый фильтр по площадкам. Пустой список — «все площадки группы»."},"generated_at":{"type":"string","format":"date-time","title":"Generated At","description":"Момент расчёта данных (UTC, ISO 8601)."},"granularity":{"type":"string","title":"Granularity","description":"Применённый размер интервала ряда: `day`, `week` или `month`."}},"type":"object","required":["published_from","published_to","source_ids","generated_at","granularity"],"title":"TimeseriesMetaV2","description":"Метаданные динамики (/v2) — с гранулярностью.","examples":[{"generated_at":"2026-07-30T09:44:52.325418Z","granularity":"month","group_id":20,"group_ids":[20],"published_from":"2026-04-01","published_to":"2026-07-30","source_ids":[0]}],"example":{"generated_at":"2026-07-30T09:44:52.325418Z","granularity":"month","group_id":20,"group_ids":[20],"published_from":"2026-04-01","published_to":"2026-07-30","source_ids":[0]}},"TimeseriesPoint":{"properties":{"period":{"type":"string","title":"Period","description":"Метка интервала. Формат зависит от `granularity`: `YYYY-MM-DD` (день), `YYYY-Www` — номер ISO-недели (неделя), `YYYY-MM` (месяц)."},"review_count":{"type":"integer","title":"Review Count","description":"Число отзывов в этом интервале."},"avg_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Rating","description":"Средняя нормализованная оценка (1–5) за интервал, округлённая до 2 знаков."},"reviews_with_text":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reviews With Text","description":"Число отзывов с непустым текстом за интервал."},"reviews_with_images":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reviews With Images","description":"Число отзывов с изображениями за интервал."},"reply_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reply Count","description":"Число отзывов с ответом организации за интервал."},"rating_distribution":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Rating Distribution","description":"Распределение отзывов интервала по оценке (ключи `1`–`5`)."},"reply_distribution":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Reply Distribution","description":"Распределение ответов организации за интервал по оценке отзыва (ключи `1`–`5`)."}},"type":"object","required":["period","review_count"],"title":"TimeseriesPoint","description":"Single timeseries data point.\n\nSparse: when review_count=0, only period and review_count are present."},"TimeseriesResponseV2":{"properties":{"meta":{"$ref":"#/components/schemas/TimeseriesMetaV2"},"series":{"items":{"$ref":"#/components/schemas/TimeseriesSeries"},"type":"array","title":"Series"},"access":{"anyOf":[{"$ref":"#/components/schemas/TrialAccessInfo"},{"type":"null"}],"description":"Ограничение выдачи. Присутствует только когда пробному доступу показали не всё — у обычного клиента ключа нет. Пробный срез применяется ДО подсчёта, поэтому без этого поля урезанные числа выглядят полными."}},"type":"object","required":["meta","series"],"title":"TimeseriesResponseV2","description":"Ответ `GET /v2/analytics/timeseries`.","examples":[{"meta":{"generated_at":"2026-07-30T09:44:52.325418Z","granularity":"month","group_id":20,"group_ids":[20],"published_from":"2026-04-01","published_to":"2026-07-30","source_ids":[0]},"series":[{"source_id":0,"source_name":"Все источники","timeseries":[{"avg_rating":4.52,"period":"2026-04","rating_distribution":{"1":4,"2":3,"3":3,"4":17,"5":58},"reply_count":52,"reply_distribution":{"1":4,"2":2,"3":2,"4":7,"5":37},"review_count":85,"reviews_with_images":10,"reviews_with_text":60},{"avg_rating":4.47,"period":"2026-05","rating_distribution":{"1":5,"2":0,"3":3,"4":17,"5":44},"reply_count":43,"reply_distribution":{"1":4,"2":0,"3":1,"4":9,"5":29},"review_count":69,"reviews_with_images":5,"reviews_with_text":51},{"avg_rating":4.27,"period":"2026-06","rating_distribution":{"1":4,"2":3,"3":15,"4":20,"5":43},"reply_count":48,"reply_distribution":{"1":3,"2":2,"3":6,"4":7,"5":30},"review_count":85,"reviews_with_images":10,"reviews_with_text":72},{"avg_rating":4.5,"period":"2026-07","rating_distribution":{"1":4,"2":1,"3":5,"4":13,"5":46},"reply_count":31,"reply_distribution":{"1":0,"2":0,"3":2,"4":6,"5":23},"review_count":69,"reviews_with_images":13,"reviews_with_text":54}]}]}],"example":{"meta":{"generated_at":"2026-07-30T09:44:52.325418Z","granularity":"month","group_id":20,"group_ids":[20],"published_from":"2026-04-01","published_to":"2026-07-30","source_ids":[0]},"series":[{"source_id":0,"source_name":"Все источники","timeseries":[{"avg_rating":4.52,"period":"2026-04","rating_distribution":{"1":4,"2":3,"3":3,"4":17,"5":58},"reply_count":52,"reply_distribution":{"1":4,"2":2,"3":2,"4":7,"5":37},"review_count":85,"reviews_with_images":10,"reviews_with_text":60},{"avg_rating":4.47,"period":"2026-05","rating_distribution":{"1":5,"2":0,"3":3,"4":17,"5":44},"reply_count":43,"reply_distribution":{"1":4,"2":0,"3":1,"4":9,"5":29},"review_count":69,"reviews_with_images":5,"reviews_with_text":51},{"avg_rating":4.27,"period":"2026-06","rating_distribution":{"1":4,"2":3,"3":15,"4":20,"5":43},"reply_count":48,"reply_distribution":{"1":3,"2":2,"3":6,"4":7,"5":30},"review_count":85,"reviews_with_images":10,"reviews_with_text":72},{"avg_rating":4.5,"period":"2026-07","rating_distribution":{"1":4,"2":1,"3":5,"4":13,"5":46},"reply_count":31,"reply_distribution":{"1":0,"2":0,"3":2,"4":6,"5":23},"review_count":69,"reviews_with_images":13,"reviews_with_text":54}]}]}},"TimeseriesSeries":{"properties":{"source_id":{"type":"integer","title":"Source Id","description":"Идентификатор площадки ряда; `0` — агрегированный ряд по всем площадкам."},"source_name":{"type":"string","title":"Source Name","description":"Название площадки; для `source_id = 0` — «Все источники»."},"timeseries":{"items":{"$ref":"#/components/schemas/TimeseriesPoint"},"type":"array","title":"Timeseries","description":"Точки ряда в хронологическом порядке."}},"type":"object","required":["source_id","source_name","timeseries"],"title":"TimeseriesSeries","description":"One source series in the timeseries response."},"TrialAccessInfo":{"properties":{"reason":{"type":"string","title":"Reason","description":"Почему выдача ограничена. `trial` — пробный доступ, `manual` — ограничение, согласованное по вашему договору."},"limit_per_link":{"type":"integer","title":"Limit Per Link","description":"Сколько последних отзывов показывается по КАЖДОЙ ссылке (площадке объекта). Ограничение на ссылку, не на аккаунт и не на группу."},"hidden_total":{"type":"integer","title":"Hidden Total","description":"Сколько отзывов уже собрано сверх показанных. Считается по всему архиву и НЕ зависит от фильтров запроса."},"message":{"type":"string","title":"Message","description":"Готовая формулировка на английском. Для интерфейса на другом языке стройте текст по числам выше."}},"type":"object","required":["reason","limit_per_link","hidden_total","message"],"title":"TrialAccessInfo","description":"Чем ограничена выдача и сколько отзывов осталось за границей.\n\nПрисутствует ТОЛЬКО когда ограничение действует. У обычного клиента ключа\n`access` в ответе нет вовсе — поэтому внутри нет флага вида `is_limited`:\nсамо наличие объекта и есть этот флаг, а поле-дубликат означало бы, что\nбывает `access` без ограничения. Клиент проверяет наличие ключа."},"WebhookCreateV2":{"properties":{"target":{"type":"string","maxLength":500,"title":"Target","description":"HTTPS endpoint that will receive `reviews.new` POSTs."},"group_ids":{"anyOf":[{"items":{"type":"integer"},"type":"array","maxItems":200,"minItems":1},{"type":"null"}],"title":"Group Ids","description":"Group ids to watch. Omit to receive reviews for ALL your groups."},"event_types":{"items":{"type":"string","enum":["reviews.new","replies.new","scrape.run.finished"]},"type":"array","maxItems":10,"minItems":1,"title":"Event Types","description":"Events: `reviews.new`, `replies.new`, `scrape.run.finished`."},"bucket_window":{"type":"string","title":"Bucket Window","description":"Delivery cadence. `immediate` — one POST shortly after each scrape batch; `daily_<hour>` — one digest per day at the given UTC hour (0–23), e.g. `daily_9` = 09:00 UTC, `daily_14` = 14:00 UTC.","default":"immediate","examples":["immediate","daily_9","daily_14"]},"filters":{"$ref":"#/components/schemas/FiltersV2"}},"type":"object","required":["target"],"title":"WebhookCreateV2","examples":[{"bucket_window":"immediate","event_types":["reviews.new"],"filters":{"allowed_ratings":[1,2,3,4,5],"allowed_sources":["yandex_maps"],"has_images":false,"has_text":false},"group_ids":[20],"target":"https://your-server.com/hooks/reviews"}],"example":{"bucket_window":"immediate","event_types":["reviews.new"],"filters":{"allowed_ratings":[1,2,3,4,5],"allowed_sources":["yandex_maps"],"has_images":false,"has_text":false},"group_ids":[20],"target":"https://your-server.com/hooks/reviews"}},"WebhookUpdateV2":{"properties":{"target":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Target"},"group_ids":{"anyOf":[{"items":{"type":"integer"},"type":"array","maxItems":200,"minItems":1},{"type":"null"}],"title":"Group Ids"},"event_types":{"anyOf":[{"items":{"type":"string","enum":["reviews.new","replies.new","scrape.run.finished"]},"type":"array","maxItems":10,"minItems":1},{"type":"null"}],"title":"Event Types"},"bucket_window":{"anyOf":[{"type":"string","description":"Delivery cadence. `immediate` — one POST shortly after each scrape batch; `daily_<hour>` — one digest per day at the given UTC hour (0–23), e.g. `daily_9` = 09:00 UTC, `daily_14` = 14:00 UTC.","examples":["immediate","daily_9","daily_14"]},{"type":"null"}],"title":"Bucket Window"},"filters":{"anyOf":[{"$ref":"#/components/schemas/FiltersV2"},{"type":"null"}]},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"WebhookUpdateV2","examples":[{"bucket_window":"daily_9","group_ids":[20],"is_active":false}],"example":{"bucket_window":"daily_9","group_ids":[20],"is_active":false}},"WebhookV2":{"properties":{"id":{"type":"integer","title":"Id"},"target":{"type":"string","title":"Target"},"group_ids":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Group Ids"},"event_types":{"items":{"type":"string","enum":["reviews.new","replies.new","scrape.run.finished"]},"type":"array","title":"Event Types"},"bucket_window":{"type":"string","title":"Bucket Window"},"filters":{"$ref":"#/components/schemas/FiltersV2"},"is_active":{"type":"boolean","title":"Is Active"},"failure_count":{"type":"integer","title":"Failure Count"},"last_failure_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Failure At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","target","group_ids","event_types","bucket_window","filters","is_active","failure_count","created_at","updated_at"],"title":"WebhookV2","description":"Вебхук (/v2). Без поля `secret` — секрет общий на аккаунт и всегда\nдоступен через `GET /v2/webhooks/secret`, а не привязан к вебхуку.","examples":[{"bucket_window":"immediate","created_at":"2026-07-19T10:00:00","event_types":["reviews.new"],"failure_count":0,"filters":{"allowed_ratings":[1,2,3,4,5],"has_images":false,"has_text":false},"group_ids":[20],"id":456,"is_active":true,"target":"https://your-server.com/hooks/reviews","updated_at":"2026-07-19T10:00:00"}],"example":{"bucket_window":"immediate","created_at":"2026-07-19T10:00:00","event_types":["reviews.new"],"failure_count":0,"filters":{"allowed_ratings":[1,2,3,4,5],"has_images":false,"has_text":false},"group_ids":[20],"id":456,"is_active":true,"target":"https://your-server.com/hooks/reviews","updated_at":"2026-07-19T10:00:00"}}},"securitySchemes":{"APIKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key"},"APIKeyCookie":{"type":"apiKey","in":"cookie","name":"rewio_session"}}},"tags":[{"name":"sources","description":"Список поддерживаемых площадок отзывов."},{"name":"groups","description":"Группы — объекты учёта отзывов (обычно один отель). Создание, просмотр, изменение и удаление групп, а также сводка по лимитам аккаунта."},{"name":"folders","description":"Папки групп (только /v2). Позволяют спросить отзывы сразу по нескольким объектам одним параметром `folder_id` — см. `GET /v2/reviews`. Глубина — два уровня: папка → подпапка → группы; одна группа может лежать в скольких угодно папках. Папки не тарифицируются и доступны в пробном периоде."},{"name":"links","description":"Ссылки внутри группы — страницы объекта на площадках, по одной на площадку. Добавление, изменение и удаление ссылок, статус сбора отзывов."},{"name":"reviews","description":"Нормализованные отзывы группы с фильтрацией, сортировкой и пагинацией."},{"name":"hidden & pinned","description":"Скрытие ненужных и закрепление нужных отзывов внутри группы; сами отзывы не меняются."},{"name":"replies","description":"Ответы на отзывы от лица организации: публикация, замена и снятие, плюс подключение площадок. Форма запроса одна для всех площадок."},{"name":"analytics","description":"Агрегированная аналитика по отзывам группы: сводка и динамика."},{"name":"webhooks","description":"Webhook-уведомления о новых отзывах.\n\n**Быстрый старт:**\n1. `POST /v2/webhooks` c `{\"target\": \"https://...\"}` — создать вебхук.\n2. `POST /v2/webhooks/{id}/test` — тестовая доставка на ваш endpoint.\n3. Принимайте POST-запросы с событием `reviews.new` (дайджест новых отзывов по вашим группам); отвечайте `2xx`.\n4. При проблемах — ответьте `2xx` быстро: мы повторяем доставку сами, а не доставленное после всех попыток пишем вам письмом.\n\n**Секрет подписи — один на аккаунт** (виден всегда): `GET /v2/webhooks/secret`; перевыпуск — `POST /v2/webhooks/rotate-secret` (старый действует ещё 24ч).\n\n**Проверка подписи** (опциональна, но рекомендуется): заголовок `X-Hrev-Signature: sha256=<hex>`, где `hex = HMAC_SHA256(secret, \"{X-Hrev-Timestamp}.\" + raw_body)`. Сравнивайте constant-time-функцией; отклоняйте запросы, у которых `|now − X-Hrev-Timestamp| > 300` секунд. Дедуплицируйте по `X-Hrev-Delivery-Id` (при ретраях один дайджест может прийти дважды). Отвечайте 2xx в течение 10 секунд; 5xx/408/429/таймаут — будет ретрай по нарастающей лестнице (30с → 30м, до 30 попыток за ~12ч), прочие 4xx — доставка помечается failed без ретраев. После 50 подряд неуспешных доставок вебхук автоматически ставится на паузу.\n\nАвторизация через **X-API-Key**."},{"name":"health","description":"Доступность API. Ключ не нужен — метод отвечает и без заголовка `X-API-Key`, поэтому им удобно отделять проблему с сетью от проблемы с ключом."},{"name":"support","description":"Написать разработчику: вопрос по API, ошибка, просьба. Работает с любым ключом, включая `read_only`. Номер аккаунта, почта и тариф подставляются сами — писать их в тексте не нужно."}]}