API Документация

Подключайте MyReviews к своим приложениям и автоматизируйте работу с компаниями, площадками, отзывами, тегами и ответами через REST API.

Base URL https://myreviews.dev/v1 Авторизация Bearer JWT

Введение

Обзор API

REST API MyReviews позволяет программно управлять организациями, площадками отзывов, самими отзывами, тегами и ответами. API подходит для интеграции с CRM, дашбордами аналитики, чат-ботами и любыми другими системами.

Base URL

Все запросы выполняются к единому адресу:

https://myreviews.dev/v1

Протокол и формат

  • Только HTTPS
  • Тело запросов – application/json
  • Все ответы – JSON
  • Кодировка – UTF-8

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

API использует JWT-токены (JSON Web Tokens). Большинство методов требуют авторизации – исключения отмечены как «публичные».

  1. Отправьте POST /v1/user/login с email и паролем.
  2. В ответе получите поле data.jwt – это ваш токен.
  3. Передавайте токен в заголовке каждого защищённого запроса:
    Authorization: Bearer <jwt>

⚠️ Безопасность

Храните JWT в защищённом месте. Не передавайте токен через URL-параметры и не встраивайте в клиентский JavaScript, доступный пользователям. Если токен скомпрометирован – смените пароль, чтобы выпустить новый.

Формат тела запросов

API построен на Yii2. Большинство POST-эндпоинтов принимают «формы» (LoginForm, RegistrationForm, Firm, Review, ReviewAnswer и т.д.). Тело таких запросов обязательно оборачивается в ключ-имя формы — иначе сервер вернёт 400 «Ошибка загрузки формы.».

JSON

{
  "FormName": {
    "field1": "value1",
    "field2": "value2"
  }
}

form-urlencoded / multipart

FormName[field1]=value1
FormName[field2]=value2

Важные моменты:

  • Query-параметры (?id=...&service=...) всегда остаются плоскими — их wrapping не затрагивает.
  • Эндпоинты с плоскими параметрами (например, firm/add-service, review/delete-tag, plan/*, idea/create) — явно отмечены в описании. Там wrapping не нужен.
  • Для загрузки файлов (firm/create, user/update, review/create) используйте multipart/form-data.
  • Поддерживается application/json и application/x-www-form-urlencoded.

❌ Неправильно

curl -X POST "https://myreviews.dev/v1/user/login" \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"..."}'
# => 400 { "message": "Ошибка загрузки формы." }

✅ Правильно

curl -X POST "https://myreviews.dev/v1/user/login" \
  -H "Content-Type: application/json" \
  -d '{"LoginForm":{"email":"user@example.com","password":"..."}}'
# => 200 { "data": { "jwt": "..." } }

Формат ответов

Успешный ответ содержит три поля:

{
  "status": 200,
  "message": "Описание результата.",
  "data": { }
}
ПолеТипОписание
statusintegerHTTP-код ответа (дублируется для удобства парсинга)
messagestringЧеловекочитаемое описание результата на русском языке
dataobject | array | string | nullПолезная нагрузка: объект, массив, строка или null при ошибке

Коды ошибок

При ошибке API возвращает HTTP-код 4xx/5xx и тело в стандартном формате Yii:

{
  "name": "Bad Request",
  "message": "Вы ввели неверный адрес электронной почты или пароль.",
  "code": 0,
  "status": 400
}
ПолеТипОписание
namestringТехническое название ошибки (Bad Request, Unauthorized, Forbidden, Not Found…)
messagestringЧеловекочитаемый текст на русском — показывайте пользователю
codeintegerВнутренний код (чаще всего 0)
statusintegerHTTP-статус

Соответствие HTTP-кодов:

КодЗначениеКогда возникает
200УспехЗапрос выполнен
400Bad RequestОшибка валидации, отсутствует wrapping формы, неверные параметры
401UnauthorizedОтсутствует или невалидный JWT-токен
403ForbiddenНет прав на запрашиваемый ресурс (например, чужая организация)
404Not FoundРесурс с указанным ID не существует
422Unprocessable EntityДанные формы не прошли валидацию (например, email занят)
429Too Many RequestsПревышен лимит запросов (актуально для user/login)
500Internal Server ErrorВнутренняя ошибка сервера — повторите позже

Пагинация

Методы, возвращающие списки (компании, отзывы), поддерживают пагинацию. Управляйте ей через query-параметры:

ПараметрТипОписание
pageintegerНомер страницы (начиная с 1)
per-pageintegerКоличество элементов на страницу (1–250, по умолчанию 100)

Метаданные пагинации возвращаются в HTTP-заголовках ответа:

ЗаголовокОписание
X-Pagination-Total-CountОбщее число записей
X-Pagination-Page-CountОбщее число страниц
X-Pagination-Current-PageТекущая страница
X-Pagination-Per-PageЗаписей на странице

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

Для защиты инфраструктуры API применяет ограничение частоты запросов (rate limiting). При превышении лимита сервер вернёт код 429.

Рекомендации

  • Кэшируйте ответы, которые меняются редко (справочник площадок, профиль)
  • Используйте per-page для получения данных пакетами, а не по одной записи
  • При получении 429 сделайте паузу и повторите запрос через несколько секунд
  • Для массовых операций лучше использовать вебхуки и уведомления через Telegram

Авторизация

Получите JWT-токен для доступа к защищённым методам API. Токен передаётся в заголовке Authorization: Bearer <jwt>.

POST /v1/user/login

Авторизация пользователя. Возвращает JWT-токен для последующих запросов.

Параметры запроса

Тело обязательно оборачивайте в ключ LoginForm — см. раздел Формат тела запросов.

ПараметрТипОписание
LoginForm[email]обязательныйstringEmail пользователя
LoginForm[password]обязательныйstringПароль

⚠️ Rate limit

Эндпоинт ограничен по IP. После серии неуспешных попыток сервер вернёт 429 Too Many Requests. Делайте паузу перед повторными запросами.

Пример ответа

200 OK
{
  "status": 200,
  "message": "Пользователь авторизован.",
  "data": {
    "jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
  }
}
400 Bad Request
{
  "name": "Bad Request",
  "message": "Вы ввели неверный адрес электронной почты или пароль.",
  "code": 0,
  "status": 400
}

Пример запроса

curl -X POST "https://myreviews.dev/v1/user/login" \
  -H "Content-Type: application/json" \
  -d '{
    "LoginForm": {
      "email": "user@example.com",
      "password": "mypassword"
    }
  }'

# Альтернатива — form-urlencoded:
curl -X POST "https://myreviews.dev/v1/user/login" \
  --data-urlencode 'LoginForm[email]=user@example.com' \
  --data-urlencode 'LoginForm[password]=mypassword'
import requests

r = requests.post("https://myreviews.dev/v1/user/login",
    json={"LoginForm": {"email": "user@example.com", "password": "mypassword"}})
jwt = r.json().get("data", {}).get("jwt")
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => 'Content-Type: application/json',
    'content' => json_encode([
        'LoginForm' => [
            'email'    => 'user@example.com',
            'password' => 'mypassword',
        ],
    ]),
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/user/login', false, $ctx),
    true
);
$jwt = $data['data']['jwt'] ?? null;
const res = await fetch("https://myreviews.dev/v1/user/login", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    LoginForm: {
      email: "user@example.com",
      password: "mypassword"
    }
  })
});
const { data } = await res.json();
const jwt = data.jwt;
POST /v1/user/join-by-email

Регистрация нового пользователя по email. После успешной регистрации возвращается JWT-токен.

Параметры запроса

Тело оборачивается в ключ RegistrationForm.

ПараметрТипОписание
RegistrationForm[email]обязательныйstringEmail нового пользователя
RegistrationForm[password]обязательныйstringПароль (не менее 6 символов)
RegistrationForm[timezone]опциональноstringТаймзона IANA, например Europe/Moscow
RegistrationForm[language]опциональноstringЯзык интерфейса, например ru
RegistrationForm[screen]опциональноstringРазрешение экрана для аналитики

Пример ответа

200 OK
{
  "status": 200,
  "message": "Пользователь зарегистрирован.",
  "data": {
    "jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
  }
}

Пример запроса

curl -X POST "https://myreviews.dev/v1/user/join-by-email" \
  -H "Content-Type: application/json" \
  -d '{
    "RegistrationForm": {
      "email": "new@example.com",
      "password": "mypassword",
      "timezone": "Europe/Moscow",
      "language": "ru"
    }
  }'
r = requests.post("https://myreviews.dev/v1/user/join-by-email",
    json={"RegistrationForm": {
        "email": "new@example.com",
        "password": "mypassword",
        "timezone": "Europe/Moscow",
        "language": "ru",
    }})
jwt = r.json().get("data", {}).get("jwt")
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => 'Content-Type: application/json',
    'content' => json_encode([
        'RegistrationForm' => [
            'email'    => 'new@example.com',
            'password' => 'mypassword',
            'timezone' => 'Europe/Moscow',
            'language' => 'ru',
        ],
    ]),
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/user/join-by-email', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/user/join-by-email", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    RegistrationForm: {
      email: "new@example.com",
      password: "mypassword",
      timezone: "Europe/Moscow",
      language: "ru"
    }
  })
});
const { data } = await res.json();
const jwt = data.jwt;
POST /v1/user/forgot-password

Восстановление пароля. Без параметра token отправляет письмо со ссылкой; с token и новым паролем — завершает восстановление.

Параметры запроса

Тело оборачивается в ключ ForgotPasswordForm.

ПараметрТипОписание
ForgotPasswordForm[email]stringEmail — для шага «отправить письмо»
ForgotPasswordForm[token]stringТокен из письма — для шага «установить новый пароль»
ForgotPasswordForm[password]stringНовый пароль — для шага «установить новый пароль»

Пример запроса

# Шаг 1: отправить письмо с токеном
curl -X POST "https://myreviews.dev/v1/user/forgot-password" \
  -H "Content-Type: application/json" \
  -d '{"ForgotPasswordForm":{"email":"user@example.com"}}'

# Шаг 2: подтвердить и установить новый пароль
curl -X POST "https://myreviews.dev/v1/user/forgot-password" \
  -H "Content-Type: application/json" \
  -d '{"ForgotPasswordForm":{"token":"abc123","password":"newpass"}}'
requests.post("https://myreviews.dev/v1/user/forgot-password",
    json={"ForgotPasswordForm": {"email": "user@example.com"}})

requests.post("https://myreviews.dev/v1/user/forgot-password",
    json={"ForgotPasswordForm": {"token": "abc123", "password": "newpass"}})
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => 'Content-Type: application/json',
    'content' => json_encode([
        'ForgotPasswordForm' => ['email' => 'user@example.com'],
    ]),
]]);
file_get_contents('https://myreviews.dev/v1/user/forgot-password', false, $ctx);
await fetch("https://myreviews.dev/v1/user/forgot-password", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    ForgotPasswordForm: { email: "user@example.com" }
  })
});

Профиль

🔒 Все методы требуют Authorization: Bearer <jwt>
GET /v1/user/profile

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

🔒 Авторизация

Пример ответа

200 OK
{
  "status": 200,
  "message": "Данные пользователя.",
  "data": {
    "id": 1,
    "email": "user@example.com",
    "name": "Иван",
    "surname": "Иванов",
    "phone": "+79001234567",
    "about": "",
    "image": null,
    "date_reg": "15.02.2024",
    "telegram_chat_id": null
  }
}

Пример запроса

curl "https://myreviews.dev/v1/user/profile" \
  -H "Authorization: Bearer JWT"
r = requests.get("https://myreviews.dev/v1/user/profile",
    headers={"Authorization": f"Bearer {jwt}"})
$ctx = stream_context_create(['http' => [
    'method' => 'GET',
    'header' => "Authorization: Bearer $jwt",
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/user/profile', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/user/profile", {
  headers: { Authorization: "Bearer " + jwt }
});
const data = await res.json();
POST /v1/user/update

Обновление данных профиля пользователя. При загрузке фото используйте multipart/form-data, иначе — JSON или application/x-www-form-urlencoded.

🔒 Авторизация

Параметры запроса

Все поля оборачиваются в ProfileForm.

ПараметрТипОписание
ProfileForm[name]stringИмя
ProfileForm[surname]stringФамилия
ProfileForm[phone]stringТелефон
ProfileForm[about]stringО себе
ProfileForm[photo_upload]fileФото, передаётся через multipart/form-data

Пример ответа

200 OK
{
  "status": 200,
  "message": "Данные пользователя отредактированы."
}

Пример запроса

# JSON — без файла
curl -X POST "https://myreviews.dev/v1/user/update" \
  -H "Authorization: Bearer JWT" \
  -H "Content-Type: application/json" \
  -d '{"ProfileForm":{"name":"Иван","surname":"Иванов"}}'

# multipart/form-data — с фото
curl -X POST "https://myreviews.dev/v1/user/update" \
  -H "Authorization: Bearer JWT" \
  -F 'ProfileForm[name]=Иван' \
  -F 'ProfileForm[surname]=Иванов' \
  -F 'ProfileForm[photo_upload]=@/path/to/avatar.jpg'
# JSON — без файла
r = requests.post("https://myreviews.dev/v1/user/update",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"ProfileForm": {"name": "Иван", "surname": "Иванов"}})

# multipart — с фото
with open("avatar.jpg", "rb") as f:
    r = requests.post("https://myreviews.dev/v1/user/update",
        headers={"Authorization": f"Bearer {jwt}"},
        data={"ProfileForm[name]": "Иван", "ProfileForm[surname]": "Иванов"},
        files={"ProfileForm[photo_upload]": f})
$payload = ['ProfileForm' => ['name' => 'Иван', 'surname' => 'Иванов']];
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => "Authorization: Bearer $jwt\r\nContent-Type: application/json",
    'content' => json_encode($payload),
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/user/update', false, $ctx),
    true
);

// multipart: используйте curl_setopt с CURLFile
// $ch = curl_init('https://myreviews.dev/v1/user/update');
// curl_setopt($ch, CURLOPT_POSTFIELDS, [
//     'ProfileForm[name]' => 'Иван',
//     'ProfileForm[photo_upload]' => new CURLFile('/path/to/avatar.jpg'),
// ]);
// JSON — без файла
await fetch("https://myreviews.dev/v1/user/update", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer " + jwt
  },
  body: JSON.stringify({
    ProfileForm: { name: "Иван", surname: "Иванов" }
  })
});

// multipart — с фото
const fd = new FormData();
fd.append("ProfileForm[name]", "Иван");
fd.append("ProfileForm[surname]", "Иванов");
fd.append("ProfileForm[photo_upload]", fileInput.files[0]);
await fetch("https://myreviews.dev/v1/user/update", {
  method: "POST",
  headers: { Authorization: "Bearer " + jwt },
  body: fd
});

Организации

🔒 Все методы, кроме view, требуют авторизации
GET /v1/firm/list

Список организаций текущего пользователя.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
per-pageintКоличество на странице (по умолчанию 100, макс. 250)

Пример ответа

200 OK
{
  "status": 200,
  "message": "Организации пользователя.",
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "ООО Рога и копыта",
      "address": "Москва, ул. Примерная, 1",
      "image": null,
      "active": true,
      "date_create": "01.01.2024"
    }
  ]
}

Пример запроса

curl "https://myreviews.dev/v1/firm/list?per-page=20" \
  -H "Authorization: Bearer JWT"
r = requests.get("https://myreviews.dev/v1/firm/list",
    params={"per-page": 20},
    headers={"Authorization": f"Bearer {jwt}"})
$ctx = stream_context_create(['http' => [
    'method' => 'GET',
    'header' => "Authorization: Bearer $jwt",
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/firm/list?per-page=20', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/firm/list?per-page=20", {
  headers: { Authorization: "Bearer " + jwt }
});
GET /v1/firm/view

Данные одной организации. Параметр id – UUID организации.

🌐 Публичный метод

Параметры запроса

ПараметрТипОписание
idобязательныйstring (UUID)UUID организации

Пример ответа

200 OK
{
  "status": 200,
  "message": "Данные организации.",
  "data": { "..." }
}

Пример запроса

curl "https://myreviews.dev/v1/firm/view?id=550e8400-e29b-41d4-a716-446655440000"
r = requests.get("https://myreviews.dev/v1/firm/view",
    params={"id": "550e8400-e29b-41d4-a716-446655440000"})
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/firm/view?id=550e8400-e29b-41d4-a716-446655440000'),
    true
);
const res = await fetch("https://myreviews.dev/v1/firm/view?id=550e8400-e29b-41d4-a716-446655440000");
const data = await res.json();
POST /v1/firm/create

Создание новой организации. При загрузке логотипа — multipart/form-data, иначе JSON.

🔒 Авторизация

Параметры запроса

Все поля оборачиваются в Firm.

ПараметрТипОписание
Firm[name]обязательныйstringНазвание организации
Firm[address]stringАдрес
Firm[type]intТип организации
Firm[photo_upload]fileЛоготип, передаётся через multipart/form-data

Пример ответа

200 OK
{
  "status": 200,
  "message": "Организация создана.",
  "id": 43
}

Пример запроса

# JSON — без файла
curl -X POST "https://myreviews.dev/v1/firm/create" \
  -H "Authorization: Bearer JWT" \
  -H "Content-Type: application/json" \
  -d '{"Firm":{"name":"Новая компания","address":"Москва, ул. Новая, 1"}}'

# multipart/form-data — с логотипом
curl -X POST "https://myreviews.dev/v1/firm/create" \
  -H "Authorization: Bearer JWT" \
  -F 'Firm[name]=Новая компания' \
  -F 'Firm[address]=Москва, ул. Новая, 1' \
  -F 'Firm[photo_upload]=@/path/to/logo.png'
r = requests.post("https://myreviews.dev/v1/firm/create",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"Firm": {"name": "Новая компания", "address": "Москва, ул. Новая, 1"}})
$payload = ['Firm' => ['name' => 'Новая компания', 'address' => 'Москва, ул. Новая, 1']];
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => "Authorization: Bearer $jwt\r\nContent-Type: application/json",
    'content' => json_encode($payload),
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/firm/create', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/firm/create", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer " + jwt
  },
  body: JSON.stringify({
    Firm: { name: "Новая компания", address: "Москва, ул. Новая, 1" }
  })
});
POST /v1/firm/update

Обновление данных организации. Параметр id – UUID организации в query-строке.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
idобязательныйstring (UUID)UUID организации (query)
Firm[name]stringНазвание
Firm[address]stringАдрес

Пример ответа

200 OK
{
  "status": 200,
  "message": "Данные организации отредактированы."
}

Пример запроса

curl -X POST "https://myreviews.dev/v1/firm/update?id=550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer JWT" \
  -H "Content-Type: application/json" \
  -d '{"Firm":{"name":"Новое название"}}'
r = requests.post("https://myreviews.dev/v1/firm/update",
    params={"id": "550e8400-e29b-41d4-a716-446655440000"},
    headers={"Authorization": f"Bearer {jwt}"},
    json={"Firm": {"name": "Новое название"}})
$payload = ['Firm' => ['name' => 'Новое название']];
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => "Authorization: Bearer $jwt\r\nContent-Type: application/json",
    'content' => json_encode($payload),
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/firm/update?id=550e8400-e29b-41d4-a716-446655440000', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/firm/update?id=550e8400-e29b-41d4-a716-446655440000", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer " + jwt
  },
  body: JSON.stringify({ Firm: { name: "Новое название" } })
});
GET /v1/firm/delete

Удаление организации. Только для владельца.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
idобязательныйstring (UUID)UUID организации

Пример ответа

200 OK
{
  "status": 200,
  "message": "Организация удалена."
}

Пример запроса

curl "https://myreviews.dev/v1/firm/delete?id=550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer JWT"
r = requests.get("https://myreviews.dev/v1/firm/delete",
    params={"id": "550e8400-e29b-41d4-a716-446655440000"},
    headers={"Authorization": f"Bearer {jwt}"})
$ctx = stream_context_create(['http' => [
    'method' => 'GET',
    'header' => "Authorization: Bearer $jwt",
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/firm/delete?id=550e8400-e29b-41d4-a716-446655440000', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/firm/delete?id=550e8400-e29b-41d4-a716-446655440000", {
  headers: { Authorization: "Bearer " + jwt }
});

Площадки отзывов

GET /v1/firm/services

Список всех доступных площадок для отзывов.

🌐 Публичный метод

Пример ответа

200 OK
{
  "status": 200,
  "message": "Сервисы",
  "data": [
    { "id": 1, "name": "Яндекс" },
    { "id": 2, "name": "Google" },
    { "id": 3, "name": "2Gis" }
  ]
}

Пример запроса

curl "https://myreviews.dev/v1/firm/services"
r = requests.get("https://myreviews.dev/v1/firm/services")
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/firm/services'),
    true
);
const res = await fetch("https://myreviews.dev/v1/firm/services");
const data = await res.json();
POST /v1/firm/add-service

Привязка площадки к организации.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
idобязательныйstring (UUID)UUID организации (query)
serviceобязательныйintID площадки (body)
urlобязательныйstringСсылка на профиль организации на площадке (body)

Пример ответа

200 OK
{
  "status": 200,
  "message": "Новая площадка добавлена.",
  "data": { "..." }
}

Пример запроса

curl -X POST "https://myreviews.dev/v1/firm/add-service?id=550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer JWT" \
  -H "Content-Type: application/json" \
  -d '{"service":1,"url":"https://yandex.ru/maps/..."}'
r = requests.post("https://myreviews.dev/v1/firm/add-service",
    params={"id": "550e8400-e29b-41d4-a716-446655440000"},
    headers={"Authorization": f"Bearer {jwt}"},
    json={"service": 1, "url": "https://yandex.ru/maps/..."})
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => "Authorization: Bearer $jwt\r\nContent-Type: application/json",
    'content' => json_encode([
        'service' => 1,
        'url'     => 'https://yandex.ru/maps/...',
    ]),
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/firm/add-service?id=550e8400-e29b-41d4-a716-446655440000', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/firm/add-service?id=550e8400-e29b-41d4-a716-446655440000", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer " + jwt
  },
  body: JSON.stringify({ service: 1, url: "https://yandex.ru/maps/..." })
});
POST /v1/firm/delete-service

Отвязка площадки от организации.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
idобязательныйstring (UUID)UUID организации (query)
serviceобязательныйintID площадки (body)

Пример ответа

200 OK
{
  "status": 200,
  "message": "Площадка удалена"
}

Пример запроса

curl -X POST "https://myreviews.dev/v1/firm/delete-service?id=550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer JWT" \
  -H "Content-Type: application/json" \
  -d '{"service":1}'
r = requests.post("https://myreviews.dev/v1/firm/delete-service",
    params={"id": "550e8400-e29b-41d4-a716-446655440000"},
    headers={"Authorization": f"Bearer {jwt}"},
    json={"service": 1})
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => "Authorization: Bearer $jwt\r\nContent-Type: application/json",
    'content' => json_encode(['service' => 1]),
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/firm/delete-service?id=550e8400-e29b-41d4-a716-446655440000', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/firm/delete-service?id=550e8400-e29b-41d4-a716-446655440000", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer " + jwt
  },
  body: JSON.stringify({ service: 1 })
});

Отзывы

GET /v1/review/index

Список отзывов с фильтрацией и пагинацией. Если firm_id не указан – отзывы по всем организациям пользователя.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
ReviewSearch[firm_id]string (UUID)UUID организации
ReviewSearch[service]intID площадки
ReviewSearch[username]stringИмя автора
ReviewSearch[date_create]stringДата (дд.мм.гггг)
sortstringСортировка: +id, -id, +date_create, -date_create
pageintНомер страницы
per-pageintКоличество на странице (1–250)
Пагинация в заголовках ответа:
  • X-Pagination-Total-Count – общее количество записей
  • X-Pagination-Page-Count – количество страниц
  • X-Pagination-Current-Page – текущая страница
  • X-Pagination-Per-Page – записей на странице

Пример запроса

curl "https://myreviews.dev/v1/review/index?ReviewSearch[firm_id]=550e8400-e29b-41d4-a716-446655440000&page=1&per-page=25" \
  -H "Authorization: Bearer JWT"
r = requests.get("https://myreviews.dev/v1/review/index",
    params={"ReviewSearch[firm_id]": "550e8400-e29b-41d4-a716-446655440000", "page": 1, "per-page": 25},
    headers={"Authorization": f"Bearer {jwt}"})
total = r.headers.get("X-Pagination-Total-Count")
$ctx = stream_context_create(['http' => [
    'method' => 'GET',
    'header' => "Authorization: Bearer $jwt",
]]);
$url = 'https://myreviews.dev/v1/review/index?'
     . http_build_query(['ReviewSearch' => ['firm_id' => '550e8400-e29b-41d4-a716-446655440000'], 'page' => 1, 'per-page' => 25]);
$data = json_decode(file_get_contents($url, false, $ctx), true);
const res = await fetch(
  "https://myreviews.dev/v1/review/index?ReviewSearch[firm_id]=550e8400-e29b-41d4-a716-446655440000&page=1&per-page=25",
  { headers: { Authorization: "Bearer " + jwt } }
);
const total = res.headers.get("X-Pagination-Total-Count");
const data = await res.json();
POST /v1/review/create

Создание отзыва об организации. Принимает multipart/form-data — можно приложить до 10 изображений. UUID организации передаётся в query как firm_id.

🌐 Публичный метод

Параметры запроса

Поля отзыва оборачиваются в Review. Массив изображений image[] передаётся плоско.

ПараметрТипОписание
firm_idобязательныйstring (UUID)UUID организации (query)
Review[message]обязательныйstringТекст отзыва
Review[rating]обязательныйintОценка от 1 до 5
Review[username]stringИмя автора
image[]file[]Изображения (до 10 файлов, multipart)

Для отрицательных отзывов (перехват негатива) используйте POST /v1/review/create-negative?firm_id={id} с тем же форматом.

Пример ответа

200 OK
{
  "status": 200,
  "message": "Отзыв создан",
  "data": { "id": 1234 }
}

Пример запроса

curl -X POST "https://myreviews.dev/v1/review/create?firm_id=550e8400-e29b-41d4-a716-446655440000" \
  -F 'Review[message]=Всё понравилось' \
  -F 'Review[rating]=5' \
  -F 'Review[username]=Иван' \
  -F 'image[]=@/path/to/photo1.jpg' \
  -F 'image[]=@/path/to/photo2.jpg'
with open("photo1.jpg", "rb") as f1, open("photo2.jpg", "rb") as f2:
    r = requests.post(
        "https://myreviews.dev/v1/review/create",
        params={"firm_id": "550e8400-e29b-41d4-a716-446655440000"},
        data={
            "Review[message]": "Всё понравилось",
            "Review[rating]": 5,
            "Review[username]": "Иван",
        },
        files=[("image[]", f1), ("image[]", f2)],
    )
$ch = curl_init('https://myreviews.dev/v1/review/create?firm_id=550e8400-e29b-41d4-a716-446655440000');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'Review[message]'  => 'Всё понравилось',
    'Review[rating]'   => 5,
    'Review[username]' => 'Иван',
    'image[0]'         => new CURLFile('/path/to/photo1.jpg'),
    'image[1]'         => new CURLFile('/path/to/photo2.jpg'),
]);
$data = json_decode(curl_exec($ch), true);
const fd = new FormData();
fd.append("Review[message]", "Всё понравилось");
fd.append("Review[rating]", "5");
fd.append("Review[username]", "Иван");
for (const file of files) fd.append("image[]", file);

await fetch(
  "https://myreviews.dev/v1/review/create?firm_id=550e8400-e29b-41d4-a716-446655440000",
  { method: "POST", body: fd }
);

Теги

GET /v1/firm/tags

Облако тегов (ключевые слова) из отзывов организации.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
idобязательныйstring (UUID)UUID организации

Пример ответа

200 OK
{
  "status": 200,
  "message": "Теги организации.",
  "data": [ "..." ]
}

Пример запроса

curl "https://myreviews.dev/v1/firm/tags?id=550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer JWT"
r = requests.get("https://myreviews.dev/v1/firm/tags",
    params={"id": "550e8400-e29b-41d4-a716-446655440000"},
    headers={"Authorization": f"Bearer {jwt}"})
$ctx = stream_context_create(['http' => [
    'method' => 'GET',
    'header' => "Authorization: Bearer $jwt",
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/firm/tags?id=550e8400-e29b-41d4-a716-446655440000', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/firm/tags?id=550e8400-e29b-41d4-a716-446655440000", {
  headers: { Authorization: "Bearer " + jwt }
});
const data = await res.json();
POST /v1/review/create-tag

Добавление тега к отзыву. Только владелец организации.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
review_idобязательныйintID отзыва (query)
ReviewTag[content]обязательныйstringТекст тега (body)

Пример ответа

200 OK
{
  "status": 200,
  "message": "Тег создан",
  "data": { "..." }
}

Пример запроса

curl -X POST "https://myreviews.dev/v1/review/create-tag?review_id=100" \
  -H "Authorization: Bearer JWT" \
  -H "Content-Type: application/json" \
  -d '{"ReviewTag":{"content":"отличный сервис"}}'
r = requests.post("https://myreviews.dev/v1/review/create-tag",
    params={"review_id": 100},
    headers={"Authorization": f"Bearer {jwt}"},
    json={"ReviewTag": {"content": "отличный сервис"}})
$payload = ['ReviewTag' => ['content' => 'отличный сервис']];
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => "Authorization: Bearer $jwt\r\nContent-Type: application/json",
    'content' => json_encode($payload),
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/review/create-tag?review_id=100', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/review/create-tag?review_id=100", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer " + jwt
  },
  body: JSON.stringify({ ReviewTag: { content: "отличный сервис" } })
});
POST /v1/review/delete-tag

Удаление ручного тега. Автоматические теги удалить нельзя.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
tag_idобязательныйintID тега

Пример ответа

200 OK
{
  "status": 200,
  "message": "Тег успешно удален"
}

Пример запроса

curl -X POST "https://myreviews.dev/v1/review/delete-tag" \
  -H "Authorization: Bearer JWT" \
  -H "Content-Type: application/json" \
  -d '{"tag_id":5}'
r = requests.post("https://myreviews.dev/v1/review/delete-tag",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"tag_id": 5})
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => "Authorization: Bearer $jwt\r\nContent-Type: application/json",
    'content' => json_encode(['tag_id' => 5]),
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/review/delete-tag', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/review/delete-tag", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer " + jwt
  },
  body: JSON.stringify({ tag_id: 5 })
});

Ответы на отзывы

POST /v1/review/create-answer

Создание и отправка ответа (или жалобы) на отзыв. Только владелец организации.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
review_idобязательныйintID отзыва (query)
complainобязательныйint0 – ответ, 1 – жалоба (query)
ReviewAnswer[message]обязательныйstringТекст ответа (body)

Пример ответа

200 OK
{
  "status": 200,
  "message": "Ответ создан",
  "data": {
    "id": 1,
    "message": "Спасибо!",
    "status": 0
  }
}

Пример запроса

curl -X POST "https://myreviews.dev/v1/review/create-answer?review_id=100&complain=0" \
  -H "Authorization: Bearer JWT" \
  -H "Content-Type: application/json" \
  -d '{"ReviewAnswer":{"message":"Спасибо за отзыв!"}}'
r = requests.post("https://myreviews.dev/v1/review/create-answer",
    params={"review_id": 100, "complain": 0},
    headers={"Authorization": f"Bearer {jwt}"},
    json={"ReviewAnswer": {"message": "Спасибо за отзыв!"}})
$payload = ['ReviewAnswer' => ['message' => 'Спасибо за отзыв!']];
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => "Authorization: Bearer $jwt\r\nContent-Type: application/json",
    'content' => json_encode($payload),
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/review/create-answer?review_id=100&complain=0', false, $ctx),
    true
);
const res = await fetch(
  "https://myreviews.dev/v1/review/create-answer?review_id=100&complain=0",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer " + jwt
    },
    body: JSON.stringify({ ReviewAnswer: { message: "Спасибо за отзыв!" } })
  }
);
GET /v1/review/generate-answer

Генерация текста ответа с помощью ИИ. Только владелец организации.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
review_idобязательныйintID отзыва
complainобязательныйint0 – ответ, 1 – жалоба

Пример ответа

200 OK
{
  "status": 200,
  "message": "Ответ сгенерирован",
  "data": "Сгенерированный текст ответа"
}

Пример запроса

curl "https://myreviews.dev/v1/review/generate-answer?review_id=100&complain=0" \
  -H "Authorization: Bearer JWT"
r = requests.get("https://myreviews.dev/v1/review/generate-answer",
    params={"review_id": 100, "complain": 0},
    headers={"Authorization": f"Bearer {jwt}"})
$ctx = stream_context_create(['http' => [
    'method' => 'GET',
    'header' => "Authorization: Bearer $jwt",
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/review/generate-answer?review_id=100&complain=0', false, $ctx),
    true
);
const res = await fetch(
  "https://myreviews.dev/v1/review/generate-answer?review_id=100&complain=0",
  { headers: { Authorization: "Bearer " + jwt } }
);
const data = await res.json();
POST /v1/review/delete-answer

Удаление ответа на отзыв. Только владелец организации.

🔒 Авторизация

Параметры запроса

ПараметрТипОписание
answer_idобязательныйintID ответа

Пример ответа

200 OK
{
  "status": 200,
  "message": "Ответ успешно удален"
}

Пример запроса

curl -X POST "https://myreviews.dev/v1/review/delete-answer" \
  -H "Authorization: Bearer JWT" \
  -H "Content-Type: application/json" \
  -d '{"answer_id":1}'
r = requests.post("https://myreviews.dev/v1/review/delete-answer",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"answer_id": 1})
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => "Authorization: Bearer $jwt\r\nContent-Type: application/json",
    'content' => json_encode(['answer_id' => 1]),
]]);
$data = json_decode(
    file_get_contents('https://myreviews.dev/v1/review/delete-answer', false, $ctx),
    true
);
const res = await fetch("https://myreviews.dev/v1/review/delete-answer", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer " + jwt
  },
  body: JSON.stringify({ answer_id: 1 })
});

Справочник площадок

Таблица соответствия числовых ID и названий площадок. Актуальный список можно получить через GET /v1/firm/services.

IDПлощадка
1Яндекс
2Google
32ГИС
4Zoon
5Yell
6MyReviews
7ПроДокторов
8Flamp
10Otzovik
12Яндекс.Услуги
13Авито
14На Поправку
15Яндекс.Бизнес
16СберЗдоровье
17Т-БАНК
18ВКонтакте
19Trustpilot
21IRecommend
22Перекрёсток
23Metro
24Магнит