Reviews API

Отзывы 2GIS, рейтинги локаций и аналитика организации — для вашей CRM, ERP, BI или любого другого продукта.

API решает три задачи:

  • читать отзывы, локации и аналитику организации;
  • синхронизировать отзывы со своей базой, забирая только изменения;
  • узнавать о событиях — новом отзыве, ответе компании, изменении рейтинга — через вебхук или ленту событий.

Адрес API: https://api.review.cerebralabs.kz/v1. Описание в формате OpenAPI: https://api.review.cerebralabs.kz/v1/openapi.json — его можно импортировать в Postman или использовать для генерации клиента.

Быстрый старт

  1. Получите API-ключ. Его создаёт владелец организации в кабинете: раздел «Интеграции» → «Создать ключ». Ключ показывается один раз.
  2. Передавайте ключ в заголовке Authorization каждого запроса:
curl -H "Authorization: Bearer rk_ВАШ_КЛЮЧ" \
  "https://api.review.cerebralabs.kz/v1/reviews?limit=1"

Ответ:

{
  "data": [
    {
      "id": "317202675",
      "location_id": "70000001012345678",
      "location_name": "Кофейня на Абая",
      "author_name": "Айгерим К.",
      "rating": 5,
      "text": "Очень вкусный кофе и приветливый бариста!",
      "created_at": "2026-11-14T14:07:35.479718Z",
      "edited_at": null,
      "changed_at": "2026-11-14T14:10:38.921527Z",
      "is_rated": true,
      "is_hidden": false,
      "likes_count": 0,
      "photo_urls": [],
      "answer": null,
      "removed_at": null
    }
  ],
  "has_more": true,
  "next_cursor": "eyJzIjoiY3JlYXRlZF9kZXNjIiwidiI6IjIwMjYtMTEtMTRUMTQ6MDc6MzUuNDc5NzE4IiwiaSI6MTk2NTh9"
}

Дальше: как держать свою базу отзывов в актуальном состоянии и как получать события на свой сервер.

Ключи и права

Ключ принадлежит одной организации и даёт доступ только к её данным. Это строка вида rk_…; храните её как пароль и не встраивайте в код, который выполняется в браузере или мобильном приложении.

У ключа есть набор прав. Запрос без нужного права получает ответ 403 с кодом insufficient_scope.

ПравоЧто разрешаетУ нового ключа
locations:readСписок локаций и их рейтингиесть
reviews:readОтзывыесть
analytics:readАналитика и история рейтингаесть
events:readЛента событийесть
webhooks:manageНастройка своих вебхуков: ключ сам подписывается на события и получает их на свой адрестолько если владелец отметил

Владелец может ограничить ключ отдельными локациями и задать срок действия. Локации, недоступные ключу, для него не существуют: запрос к ним отвечает 404.

Отозванный или просроченный ключ сразу перестаёт приниматься (401, код invalid_api_key). Вебхуки, которые система настроила этим ключом, при отзыве отключаются.

Общие правила

Время и идентификаторы

  • Моменты времени — в UTC, формат ISO 8601: 2026-11-14T14:07:35.479718Z. В параметрах запроса можно указывать время со смещением (…+05:00); время без смещения считается UTC.
  • Идентификаторы отзывов и локаций — строки. Идентификатор локации совпадает с номером карточки в 2GIS.
  • Поле без значения приходит как null, а не пропадает из ответа.

Листание

Списки отдаются частями. В ответе есть has_more и указатель на продолжение: next_cursor у отзывов, next_after у событий. Передайте его в следующем запросе и повторяйте, пока has_more равно true. Размер части задаёт параметр limit.

Лимит запросов

Не больше 120 запросов в минуту на ключ. В каждом ответе есть заголовки X-RateLimit-Limit, X-RateLimit-Remaining и X-RateLimit-Reset (через сколько секунд счётчик обнулится). При превышении приходит ответ 429 с заголовком Retry-After — подождите указанное число секунд и повторите запрос.

Ошибки

Любая ошибка приходит в одном формате. Опирайтесь в коде на поле code: оно не меняется, а текст message может уточняться.

{
  "error": {
    "code": "insufficient_scope",
    "message": "Ключу не разрешено: analytics:read",
    "request_id": "req_9584d029f63a754f"
  }
}
HTTPcodeЧто произошло
400invalid_cursorКурсор получен из запроса с другим sort или повреждён
401unauthorizedКлюч не передан
401invalid_api_keyКлюч неизвестен, отозван или просрочен
403insufficient_scopeУ ключа нет права на этот метод
403organization_disabledОрганизация отключена от сервиса
404not_foundОбъекта нет или он недоступен ключу — эти случаи намеренно не различаются
409conflictДействие сейчас невозможно: например, достигнут предел числа вебхуков
422validation_errorНекорректные параметры; в details — список полей с пояснениями
429rate_limitedПревышен лимит запросов
500internal_errorСбой на нашей стороне; сообщите поддержке значение request_id

Номер запроса есть и в заголовке X-Request-Id каждого ответа, в том числе успешного.

Свежесть данных

  • Новый отзыв появляется в API в течение примерно 5 минут после публикации в 2GIS.
  • Правки и ответы компании на свежих отзывах замечаются так же быстро. На старых отзывах, а также пропажа отзыва из 2GIS — раз в сутки, ночью.
  • Отзывы новой локации загружаются в течение нескольких минут после её подключения; пока загрузка идёт, у локации initial_load_done равно false.

Синхронизация отзывов

Чтобы держать у себя копию отзывов, не нужно каждый раз выгружать всё заново. Запросите отзывы в порядке их изменения — sort=changed_asc — и сохраните последний next_cursor. В следующий раз запрос с этим курсором вернёт только то, что изменилось: новые отзывы, правки, ответы компании, пропажи.

import pathlib
import time

import requests

API = "https://api.review.cerebralabs.kz/v1"
HEADERS = {"Authorization": "Bearer rk_ВАШ_КЛЮЧ"}
CURSOR_FILE = pathlib.Path("reviews_cursor.txt")  # где хранить курсор между запусками


def sync(save):
    """Забирает всё, что изменилось с прошлого запуска. save(review) — создать или обновить отзыв по review["id"]."""
    cursor = CURSOR_FILE.read_text().strip() if CURSOR_FILE.exists() else None
    while True:
        params = {"sort": "changed_asc", "limit": 200}
        if cursor:
            params["cursor"] = cursor
        response = requests.get(f"{API}/reviews", headers=HEADERS, params=params, timeout=30)
        if response.status_code == 429:  # превышен лимит — ждём столько, сколько просит сервер
            time.sleep(int(response.headers["Retry-After"]))
            continue
        response.raise_for_status()
        page = response.json()

        for review in page["data"]:
            save(review)  # у пропавшего из 2GIS отзыва заполнено review["removed_at"]

        if page["next_cursor"]:
            cursor = page["next_cursor"]
            CURSOR_FILE.write_text(cursor)  # сохраняем после каждой страницы: сбой не заставит начинать заново
        if not page["has_more"]:
            break
  • В этом режиме next_cursor возвращается всегда, даже когда изменений нет, — сохраняйте его после каждой части.
  • Один и тот же отзыв может прийти повторно, если он изменился ещё раз. Создавайте или обновляйте запись по id.
  • Отзывы, пропавшие из 2GIS, в этом режиме тоже приходят — с заполненным removed_at. Если отзыв вернётся, он придёт снова с removed_at: null.
  • Первая выгрузка большой организации — это десятки запросов. Обрабатывайте ответ 429, как в примере.

Если вашей системе важны не сами отзывы, а факты — «появился отзыв», «компания ответила», — используйте события.

События

Сервис записывает каждое замеченное изменение как событие. Получать события можно двумя способами — формат одинаковый:

  • вебхук — сервис сам отправляет событие на ваш сервер;
  • лента GET /v1/events — вы периодически запрашиваете новые события, передавая в after значение next_after из прошлого ответа. Подходит, если ваш сервер не может принимать входящие запросы.
ТипКогда происходит
review.createdНовый отзыв
review.updatedОтзыв изменён: текст, оценка, учёт в рейтинге; либо ответ компании убран
review.answeredКомпания ответила на отзыв или изменила ответ
review.removedОтзыв пропал из 2GIS
review.restoredПропавший отзыв снова в 2GIS
location.rating_changedИзменился рейтинг карточки 2GIS

Формат

У события есть id (растёт в порядке появления), type, момент occurred_at, локация и содержимое data.

Новый отзыв — в data.review тот же объект, что отдаёт GET /v1/reviews:

{
  "id": 1042,
  "type": "review.created",
  "occurred_at": "2026-11-14T14:10:38.921527Z",
  "location_id": "70000001012345678",
  "location_name": "Кофейня на Абая",
  "data": {
    "review": {
      "id": "317202675",
      "location_id": "70000001012345678",
      "location_name": "Кофейня на Абая",
      "author_name": "Айгерим К.",
      "rating": 5,
      "text": "Очень вкусный кофе и приветливый бариста!",
      "created_at": "2026-11-14T14:07:35.479718Z",
      "edited_at": null,
      "changed_at": "2026-11-14T14:10:38.921527Z",
      "is_rated": true,
      "is_hidden": false,
      "likes_count": 0,
      "photo_urls": [],
      "answer": null,
      "removed_at": null
    }
  }
}

У событий review.updated и review.answered дополнительно есть changed — что изменилось, и previous — прежние значения этих полей:

{
  "id": 1057,
  "type": "review.answered",
  "occurred_at": "2026-11-14T15:05:40.310000Z",
  "location_id": "70000001012345678",
  "location_name": "Кофейня на Абая",
  "data": {
    "review": {
      "id": "317202675",
      "location_id": "70000001012345678",
      "location_name": "Кофейня на Абая",
      "author_name": "Айгерим К.",
      "rating": 5,
      "text": "Очень вкусный кофе и приветливый бариста!",
      "created_at": "2026-11-14T14:07:35.479718Z",
      "edited_at": null,
      "changed_at": "2026-11-14T15:05:40.310000Z",
      "is_rated": true,
      "is_hidden": false,
      "likes_count": 0,
      "photo_urls": [],
      "answer": {
        "text": "Спасибо за отзыв! Ждём вас снова.",
        "answered_at": "2026-11-14T15:02:11.120000Z",
        "first_answered_at": "2026-11-14T15:02:11.120000Z"
      },
      "removed_at": null
    },
    "changed": [
      "answer"
    ],
    "previous": {
      "answer": null
    }
  }
}

Изменение рейтинга карточки:

{
  "id": 1060,
  "type": "location.rating_changed",
  "occurred_at": "2026-11-14T15:10:02.500000Z",
  "location_id": "70000001012345678",
  "location_name": "Кофейня на Абая",
  "data": {
    "rating": 4.6,
    "previous_rating": 4.7,
    "rating_confirmed": true,
    "reviews_count": 321
  }
}

Со временем появятся новые типы событий и новые поля. Незнакомые типы пропускайте, незнакомые поля игнорируйте — тогда обновления API не сломают вашу интеграцию.

Локация показывает события только с момента её подключения к организации; события за время первичной загрузки отзывов не создаются.

Вебхуки

Вебхук — это адрес на вашем сервере, на который сервис отправляет события запросом POST с телом в формате JSON. Настроить его можно двумя способами:

  • владелец организации — в кабинете, раздел «Интеграции» → «Добавить вебхук»;
  • ваша система сама — запросом к API. Ключу нужно право webhooks:manage:
curl -X POST "https://api.review.cerebralabs.kz/v1/webhooks" \
  -H "Authorization: Bearer rk_ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://crm.example.com/hooks/reviews", "event_types": ["review.created", "review.answered"]}'

В ответе придёт secret — секрет для проверки подписи. Он показывается один раз; при необходимости его можно сменить методом rotate-secret.

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

Требования к получателю

  • Адрес — только https, с действующим сертификатом, на сервере, доступном из интернета по IPv4.
  • На сообщение нужно ответить кодом 2xx за 10 секунд. Отвечайте сразу, а долгую обработку выполняйте в фоне.
  • Перенаправления не выполняются: ответ 3xx считается неудачей.

Повторы и порядок

  • Если сообщение не доставлено, оно отправляется повторно: через 1 мин, 5 мин, 15 мин, 1 ч, 3 ч, 6 ч, 12 ч, 24 ч — всего до 9 попыток примерно за двое суток.
  • Повтор несёт тот же заголовок webhook-id. Запоминайте принятые значения и не обрабатывайте сообщение дважды.
  • Пока получатель отвечает, события приходят по порядку. После сбоев порядок может нарушаться — ориентируйтесь на id и occurred_at события.
  • Адрес, на который ничего не удаётся доставить 3 дня подряд, отключается; ответ 410 отключает его сразу. События за время отключения не досылаются — заберите их из ленты GET /v1/events.

Проверка связи

Метод POST /v1/webhooks/{id}/test (или кнопка «Отправить проверочное сообщение» в кабинете) отправляет событие типа ping. Результат и ответ вашего сервера видны в журнале доставок: GET /v1/webhooks/{id}/deliveries.

Проверка подписи

Адрес вебхука могут узнать посторонние, поэтому каждое сообщение подписано. Проверяйте подпись, прежде чем доверять содержимому. Подпись соответствует открытой спецификации Standard Webhooks — для неё есть готовые библиотеки на многих языках.

ЗаголовокЗначение
webhook-idНомер сообщения. Не меняется при повторной отправке
webhook-timestampВремя отправки, секунды Unix
webhook-signaturev1,<подпись>; подписей может быть несколько через пробел

Как проверить:

  1. Соберите строку {webhook-id}.{webhook-timestamp}.{тело запроса}. Тело берите как есть, байт в байт, — не разобранный и заново собранный JSON.
  2. Ключ — это часть секрета после whsec_, декодированная из base64.
  3. Посчитайте HMAC-SHA256 от строки с этим ключом, закодируйте в base64 и сравните с подписью из заголовка.
  4. Отклоняйте сообщения, у которых webhook-timestamp отличается от текущего времени больше чем на 5 минут.

Python

import base64
import hashlib
import hmac
import time


def verify_webhook(secret: str, headers, body: bytes, tolerance: int = 300) -> bool:
    """secret — whsec_…; headers — заголовки запроса (имена в нижнем регистре); body — тело запроса как есть."""
    message_id = headers["webhook-id"]
    timestamp = headers["webhook-timestamp"]
    if abs(time.time() - int(timestamp)) > tolerance:
        return False  # слишком старое сообщение: возможно, кто-то повторяет перехваченный запрос

    key = base64.b64decode(secret.removeprefix("whsec_"))
    signed = f"{message_id}.{timestamp}.".encode() + body
    expected = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()

    # В заголовке может быть несколько подписей через пробел: "v1,<подпись> v1,<подпись>"
    received = [part.split(",", 1)[1] for part in headers["webhook-signature"].split() if part.startswith("v1,")]
    return any(hmac.compare_digest(expected, candidate) for candidate in received)
Node.js
const crypto = require("crypto");

// body — тело запроса как есть (Buffer или строка), до JSON.parse; имена заголовков в нижнем регистре
function verifyWebhook(secret, headers, body, tolerance = 300) {
  const id = headers["webhook-id"];
  const timestamp = headers["webhook-timestamp"];
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > tolerance) return false; // слишком старое сообщение

  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const expected = crypto.createHmac("sha256", key).update(`${id}.${timestamp}.`).update(body).digest("base64");

  // В заголовке может быть несколько подписей через пробел: "v1,<подпись> v1,<подпись>"
  return headers["webhook-signature"].split(" ").some((part) => {
    const [version, signature] = part.split(",");
    if (version !== "v1" || !signature) return false;
    const a = Buffer.from(signature);
    const b = Buffer.from(expected);
    return a.length === b.length && crypto.timingSafeEqual(a, b);
  });
}

module.exports = { verifyWebhook };
PHP
<?php

// $body — тело запроса как есть: file_get_contents('php://input'); имена заголовков в нижнем регистре
function verify_webhook(string $secret, array $headers, string $body, int $tolerance = 300): bool
{
    $id = $headers['webhook-id'];
    $timestamp = $headers['webhook-timestamp'];
    if (abs(time() - (int) $timestamp) > $tolerance) {
        return false; // слишком старое сообщение
    }

    $key = base64_decode(substr($secret, strlen('whsec_')));
    $expected = base64_encode(hash_hmac('sha256', "{$id}.{$timestamp}.{$body}", $key, true));

    // В заголовке может быть несколько подписей через пробел: "v1,<подпись> v1,<подпись>"
    foreach (explode(' ', $headers['webhook-signature']) as $part) {
        [$version, $signature] = array_pad(explode(',', $part, 2), 2, '');
        if ($version === 'v1' && hash_equals($expected, $signature)) {
            return true;
        }
    }
    return false;
}
Приёмник целиком (Python, Flask)
from flask import Flask, request

from verify import verify_webhook  # функция из примера выше

app = Flask(__name__)
SECRET = "whsec_ВАШ_СЕКРЕТ"
seen = set()  # в настоящей системе — таблица в базе


@app.post("/hooks/reviews")
def reviews_hook():
    body = request.get_data()  # именно сырое тело: подпись считается по нему
    headers = {name.lower(): value for name, value in request.headers.items()}
    if not verify_webhook(SECRET, headers, body):
        return "", 401

    if headers["webhook-id"] in seen:  # повтор уже принятого сообщения
        return "", 204
    seen.add(headers["webhook-id"])

    event = request.get_json()
    if event["type"] == "review.created":
        review = event["data"]["review"]
        print(f'Новый отзыв {review["rating"]}★ в «{event["location_name"]}»: {review["text"]}')
    # Незнакомые типы событий просто пропускайте: со временем появятся новые

    return "", 204  # отвечайте сразу, долгую обработку выносите в фон

С готовой библиотекой проверка занимает одну строку — например, Webhook(secret).verify(body, headers) в пакете standardwebhooks для Python.

Справочник методов

Пути указаны относительно адреса https://api.review.cerebralabs.kz. Этот же справочник в машинном виде — описание OpenAPI.

Организация

GET /v1/organization

Организация, которой принадлежит ключ · любой действующий ключ

Ответ

200 — JSON: Organization

Локации

GET /v1/locations

Список локаций · любой действующий ключ

Действующие локации организации с рейтингом карточки 2GIS и числом отзывов.

Ответ

200 — JSON: список: Location

GET /v1/locations/{location_id}

Одна локация · любой действующий ключ

Параметры

ИмяТипОписание
location_idстрокав пути

Ответ

200 — JSON: Location

Отзывы

GET /v1/reviews

Список отзывов · любой действующий ключ

Отзывы организации.

Лента. sort=created_desc (по умолчанию) — от новых к старым; листайте по next_cursor, пока has_more.

Синхронизация. sort=changed_asc отдаёт отзывы в порядке, в котором сервис замечал изменения (появление, правка, ответ компании, пропажа из 2GIS). Пройдите список до конца и сохраните последний next_cursor; следующий запрос с ним вернёт только то, что изменилось с тех пор.

Параметры

ИмяТипОписание
location_idсписок: строкаОдна или несколько локаций; по умолчанию все
ratingсписок: целое числоОценки от 1 до 5, можно несколько
created_sinceдата и времяНаписанные не раньше этого момента (ISO 8601)
created_untilдата и времяНаписанные раньше этого момента
changed_sinceдата и времяИзменившиеся не раньше этого момента
has_answerда/нетtrue — с ответом компании, false — без ответа
searchстрокаПоиск по тексту отзыва; до 200 символов
include_removedда/нетПоказывать отзывы, пропавшие из 2GIS. По умолчанию: нет для sort=created_desc, да для sort=changed_asc — иначе при синхронизации пропажа отзыва останется незамеченной
sortстрокаcreated_desc — сначала новые; changed_asc — в порядке изменения, для синхронизации; одно из: created_desc, changed_asc; по умолчанию created_desc
limitцелое числоот 1 до 200; по умолчанию 50
cursorстрокаЗначение next_cursor из предыдущего ответа

Ответ

200 — JSON: ReviewList

GET /v1/reviews/{review_id}

Один отзыв · любой действующий ключ

Отзыв по его ID, в том числе пропавший из 2GIS.

Параметры

ИмяТипОписание
review_idстрокав пути

Ответ

200 — JSON: Review

События

GET /v1/events

Лента событий · любой действующий ключ

Что изменилось в отзывах и рейтингах: новые отзывы, правки, ответы компании, пропажи.

События идут в порядке появления. Запомните next_after из ответа и передайте его в after при следующем запросе — получите только новые события. Локация показывает события с момента её подключения к организации.

Параметры

ИмяТипОписание
afterцелое числоID последнего полученного события; 0 — с начала; не меньше 0; по умолчанию 0
sinceдата и времяТолько события не раньше этого момента (ISO 8601)
typeсписок: строкаТипы событий, можно несколько
location_idсписок: строкаОдна или несколько локаций; по умолчанию все
limitцелое числоот 1 до 500; по умолчанию 100

Ответ

200 — JSON: EventList

Аналитика

GET /v1/analytics/summary

Сводка за период · любой действующий ключ

Показатели за период, те же показатели за предыдущий период и таблица локаций с тремя рейтингами.

В расчёты идут только отзывы, которые сейчас есть в 2GIS и учитываются им в рейтинге.

Параметры

ИмяТипОписание
date_fromдатаНачало периода; по умолчанию 30 дней назад
date_toдатаКонец периода включительно; по умолчанию сегодня
daysцелое числоПоследние N дней, включая сегодня; от 1 до 1098
location_idсписок: строкаЛокации; по умолчанию все

Ответ

200 — JSON: Summary

GET /v1/analytics/timeseries

Динамика по дням, неделям или месяцам · любой действующий ключ

Число отзывов, средняя оценка и негатив по дням, неделям или месяцам.

Параметры

ИмяТипОписание
date_fromдата
date_toдата
daysцелое числоПоследние N дней, включая сегодня; от 1 до 1098
location_idсписок: строка
granularityстрокаодно из: day, week, month; по умолчанию day

Ответ

200 — JSON: список: TimeseriesPoint

GET /v1/locations/{location_id}/rating-history

История рейтинга карточки 2GIS · любой действующий ключ

Как менялся рейтинг карточки в 2GIS. Снимки делаются раз в сутки с момента подключения сбора.

Параметры

ИмяТипОписание
location_idстрокав пути
date_fromдата
date_toдата
daysцелое числоПоследние N дней, включая сегодня; от 1 до 1098

Ответ

200 — JSON: список: RatingHistoryPoint

Вебхуки

GET /v1/webhooks

Вебхуки этого ключа · любой действующий ключ

Ответ

200 — JSON: список: Webhook

POST /v1/webhooks

Создать вебхук · любой действующий ключ

Сервис начнёт отправлять события организации на указанный адрес.

Сообщение — тот же объект, что отдаёт GET /v1/events. Оно подписано по спецификации Standard Webhooks (заголовки webhook-id, webhook-timestamp, webhook-signature); секрет для проверки подписи есть только в ответе на этот запрос. На сообщение нужно ответить кодом 2xx за 10 секунд, иначе оно будет отправлено повторно (до 9 попыток за двое суток).

Тело запроса

JSON: WebhookInput

Ответ

201 — JSON: WebhookCreated

GET /v1/webhooks/{webhook_id}

Один вебхук · любой действующий ключ

Параметры

ИмяТипОписание
webhook_idцелое числов пути

Ответ

200 — JSON: Webhook

PATCH /v1/webhooks/{webhook_id}

Изменить вебхук · любой действующий ключ

Параметры

ИмяТипОписание
webhook_idцелое числов пути

Тело запроса

JSON: WebhookPatch

Ответ

200 — JSON: Webhook

DELETE /v1/webhooks/{webhook_id}

Удалить вебхук · любой действующий ключ

Параметры

ИмяТипОписание
webhook_idцелое числов пути

Ответ

204 — без тела

POST /v1/webhooks/{webhook_id}/rotate-secret

Сменить секрет подписи · любой действующий ключ

Прежний секрет перестаёт действовать сразу.

Параметры

ИмяТипОписание
webhook_idцелое числов пути

Ответ

200 — JSON: WebhookSecret

POST /v1/webhooks/{webhook_id}/test

Отправить проверочное сообщение · любой действующий ключ

На адрес уйдёт сообщение типа ping. Результат — в журнале доставок через несколько секунд.

Параметры

ИмяТипОписание
webhook_idцелое числов пути

Ответ

202 — JSON: Delivery

GET /v1/webhooks/{webhook_id}/deliveries

Журнал доставок · любой действующий ключ

Что и когда отправлялось на адрес, от новых к старым.

Параметры

ИмяТипОписание
webhook_idцелое числов пути
statusстрокаодно из: pending, succeeded, failed
beforeцелое числоПоказать отправки старше этой (её id)
limitцелое числоот 1 до 200; по умолчанию 50

Ответ

200 — JSON: DeliveryList

Объекты

Delivery

ПолеТипОписание
idцелое число
event_idцелое число или nullID события; null у проверочного сообщения
typeстрока
statusстрокаpending | succeeded | failed
attemptsцелое число
next_attempt_atстрока или null
last_attempt_atстрока или null
last_status_codeцелое число или null
last_errorстрока или null
created_atстрока
delivered_atстрока или null

DeliveryList

ПолеТипОписание
dataсписок: Delivery
has_moreда/нет

ErrorBody

ПолеТипОписание
codeстрокаКод ошибки для программы: unauthorized, invalid_api_key, insufficient_scope, not_found, validation_error, invalid_cursor, rate_limited, internal_error и др.
messageстрокаПояснение для человека
request_idстрокаНомер запроса — назовите его поддержке
detailsсписок: объект или null

ErrorResponse

ПолеТипОписание
errorErrorBody

Event

ПолеТипОписание
idцелое числоРастёт в порядке появления событий; годится как курсор (параметр after)
typeстрокаreview.created | review.updated | review.answered | review.removed | review.restored | location.rating_changed
occurred_atстрокаКогда сервис заметил изменение
location_idстрока
location_nameстрока
dataобъектДля событий review.*: review — отзыв на момент события; у review.updated и review.answered ещё changed (что изменилось) и previous (прежние значения). Для location.rating_changed: rating, previous_rating, rating_confirmed, reviews_count

EventList

ПолеТипОписание
dataсписок: Event
has_moreда/нет
next_afterцелое числоПередайте в параметре after, чтобы получить следующие события

Location

ПолеТипОписание
idстрокаID локации — он же номер карточки в 2GIS
nameстрокаНазвание локации внутри организации
card_nameстрока или nullНазвание карточки по данным 2GIS, если оно известно
cityстрока или null
addressстрока или null
rating_2gisчисло или nullРейтинг карточки в 2GIS, как на карте
reviews_count_2gisцелое число или nullЧисло отзывов по данным 2GIS
rating_2gis_updated_atстрока или null
rating_targetчисло или nullЦель по рейтингу, заданная владельцем
reviews_countцелое числоОтзывов в сервисе (без пропавших из 2GIS)
last_review_atстрока или null
initial_load_doneда/нетfalse — отзывы новой локации ещё загружаются (до нескольких минут)

LocationSummary

ПолеТипОписание
location_idстрока
nameстрока
cityстрока или null
rating_2gisчисло или nullРейтинг карточки в 2GIS, как на карте
reviews_count_2gisцелое число или null
rating_calculatedчисло или nullРасчётный рейтинг за последние 90 дней со сглаживанием
avg_rating_90dчисло или nullОбычное среднее за те же 90 дней, без сглаживания
reviews_90dцелое число
rating_targetчисло или null
target_gapчисло или nullРасчётный рейтинг минус цель; отрицательное — ниже цели
target_gap_2gisчисло или nullРейтинг 2GIS минус цель
periodPeriodMetrics
previousPeriodMetrics

Organization

ПолеТипОписание
idстрока
nameстрока
locations_countцелое числоЧисло локаций, доступных этому ключу

PeriodMetrics

ПолеТипОписание
reviewsцелое число
avg_ratingчисло или null
negativeцелое числоОтзывов с оценкой 3★ и ниже
negative_shareчисло или nullДоля негатива, от 0 до 1
response_rateчисло или nullДоля отзывов с ответом компании, от 0 до 1
negative_response_rateчисло или nullДоля негативных отзывов с ответом, от 0 до 1
unanswered_negativeцелое число
median_response_hoursчисло или nullМедианное время до ответа компании, часы
distributionобъектЧисло отзывов по оценкам: ключи '1'..'5'

RatingHistoryPoint

ПолеТипОписание
dateдата
ratingчисло или null
reviews_countцелое число или null

RatingWindow

ПолеТипОписание
avgчисло или null
reviewsцелое число
daysцелое число

Review

ПолеТипОписание
idстрока
location_idстрока
location_nameстрока
author_nameстрока или null
ratingцелое число или nullОценка от 1 до 5
textстрока или null
created_atстрока или nullКогда отзыв написан
edited_atстрока или nullКогда автор его правил, по данным 2GIS
changed_atстрокаКогда сервис заметил последнее изменение отзыва: появление, правку, ответ, пропажу, возврат
is_ratedда/нет или nullУчитывает ли 2GIS отзыв в рейтинге карточки
is_hiddenда/нет или null
likes_countцелое число
photo_urlsсписок: строка
answerReviewAnswer или nullОфициальный ответ компании; null — ответа нет
removed_atстрока или nullКогда отзыв пропал из 2GIS; null — отзыв на месте

ReviewAnswer

ПолеТипОписание
textстрока или null
answered_atстрока или nullДата текущего ответа по данным 2GIS
first_answered_atстрока или nullКогда компания ответила впервые; не меняется, если ответ переписали

ReviewList

ПолеТипОписание
dataсписок: Review
has_moreда/нет
next_cursorстрока или nullПередайте в параметре cursor, чтобы получить продолжение. При sort=changed_asc возвращается всегда: сохраните его и в следующий раз получите только новые изменения

Summary

ПолеТипОписание
date_fromдата
date_toдата
previous_fromдата
previous_toдата
timezoneстрокаЧасовой пояс, в котором считаются сутки
periodPeriodMetrics
previousPeriodMetricsТе же показатели за предыдущий период такой же длины
rating_windowRatingWindowСредняя оценка по выбранным локациям за 90 дней
locationsсписок: LocationSummary

TimeseriesPoint

ПолеТипОписание
dateдатаНачало дня, недели (понедельник) или месяца
reviewsцелое число
avg_ratingчисло или null
negativeцелое число

Webhook

ПолеТипОписание
idцелое число
urlстрока
descriptionстрока или null
event_typesсписок: строкаПусто — все события
location_idsсписок: строка или nullnull — все локации организации
is_activeда/нет
disabled_reasonстрока или nullmanual — отключён вручную; failures — несколько дней ничего не удавалось доставить; gone — получатель ответил 410; key_revoked — отозван ключ
failing_sinceстрока или nullС какого момента ничего не удаётся доставить
last_success_atстрока или null
last_failure_atстрока или null
created_atстрока

WebhookCreated

ПолеТипОписание
secretстрокаСекрет для проверки подписи (Standard Webhooks). Показывается один раз
idцелое число
urlстрока
descriptionстрока или null
event_typesсписок: строкаПусто — все события
location_idsсписок: строка или nullnull — все локации организации
is_activeда/нет
disabled_reasonстрока или nullmanual — отключён вручную; failures — несколько дней ничего не удавалось доставить; gone — получатель ответил 410; key_revoked — отозван ключ
failing_sinceстрока или nullС какого момента ничего не удаётся доставить
last_success_atстрока или null
last_failure_atстрока или null
created_atстрока

WebhookInput

ПолеТипОписание
url обязательноестрокаАдрес получателя: только https, публичный сервер. До 2000 символов
descriptionстрока или nullДо 200 символов
event_typesсписок: строкаТипы событий; пусто — все
location_idsсписок: строка или nullЛокации; не задано — все доступные ключу

WebhookPatch

ПолеТипОписание
urlстрока или nullДо 2000 символов
descriptionстрока или nullДо 200 символов
event_typesсписок: строка или null
location_idsсписок: строка или nullПустой список — все доступные ключу
is_activeда/нет или null

WebhookSecret

ПолеТипОписание
secretстрокаСекрет для проверки подписи (Standard Webhooks). Показывается один раз

Версии и изменения

Версия API указана в пути: /v1. В пределах версии мы можем добавлять методы, необязательные параметры, поля в ответах и типы событий. Удалять и переименовывать поля, менять их смысл и тип — только в новой версии; прежняя при этом продолжит работать, а о сроках её отключения мы предупредим заранее.

ДатаЧто изменилось
14 ноября 2026Первая версия: организация, локации, отзывы, аналитика, лента событий, вебхуки