# Начало работы с REST API
Sales Ninja предоставляет единый внешний REST API. Он покрывает три задачи:
- Применение вариантов стека «Сайт» (персонализации, A/B-тесты, действия по правилам) — runtime-методы
из мобильного приложения, SSR или бэкенда: какой вариант показать пользователю. На сайте тот же контур закрывает JS API
onPersonalization. - Передача конверсий — runtime-метод, которым вы программно сообщаете Sales Ninja, что пользователь достиг бизнес-цели. Применяется там, где конверсия происходит не на сайте (CRM, мобильное приложение, бэкенд, импорт исторических данных).
- Управление сущностями — программный CRUD над персонализациями, A/B-тестами, опросами, rule-based действиями, моделируемыми конверсиями, аудиторными сегментами, антиботами, smart-фидами. Сюда же относится сборка условий показа V2 и запуск отчётов статистики.
Все три блока работают на одних и тех же «рельсах»: общий хост, общая авторизация (X-SN-TOKEN), общий формат
ошибок (RFC 7807), общее версионирование. Один токен открывает доступ ко всем эндпоинтам в рамках одного
проекта.
# Базовые URL
| Группа | Префикс |
|---|---|
| Runtime | https://api.sales-ninja.me/public/{source}/v1.0/ |
| Management | https://api.sales-ninja.me/public/api/v1/manage/ |
| Конструкторы | https://api.sales-ninja.me/public/api/v1/conditions/ и analytics/ |
| Статистика | https://api.sales-ninja.me/public/api/v1/analytics/ |
Параметр {source} в runtime-маршрутах — это идентификатор источника вызова, чтобы Sales Ninja понимал, откуда
пришла информация (мобильное приложение, серверный код, импорт из CRM и т.п.). Допустимые значения возвращает
служба поддержки; неизвестный source отдаёт 400.
# Авторизация
Каждый запрос обязан содержать заголовок X-SN-TOKEN с токеном проекта:
X-SN-TOKEN: 9f5a4c1d-7b8e-4f3a-9d2c-1e7b0a4c5d6f
Токен уникален на уровне проекта и виден в настройках проекта в админке Sales Ninja:

Правила:
- Один токен = один проект. Все запросы с этим токеном считаются выполненными в контексте этого проекта.
projectIdнигде не передаётся в теле или query. Он всегда выводится из токена; любое значение в теле игнорируется. Это исключает класс ошибок, когда токен от одного проекта используется для записи в другой.- Токен надо хранить в секрете. Не публиковать в клиентском JS, не коммитить в репозиторий — он даёт право на запись и удаление сущностей.
- Если токен невалиден или отсутствует —
401 Unauthorizedсapplication/problem+json. - Если проект приостановлен (suspended/archived) —
403 Forbiddenсcode: "ProjectSuspended".
# Формат ошибок
Все ошибки возвращаются по стандарту RFC 7807 (Problem Details) с Content-Type: application/problem+json:
{
"type": "about:blank",
"status": 422,
"title": "Unprocessable Entity",
"detail": "Title must be non-empty.",
"instance": "/public/api/v1/manage/personalizations",
"traceId": "0HMV3K9T6QJ4F:00000003",
"code": "ValidationFailed",
"hint": "Set request body 'title' to a non-empty string.",
"paramName": "title",
"jsonPath": "$.title",
"validEnumValues": ["Equal", "NotEqual", "Greater", "Less", "Contains"]
}
Поля:
| Поле | Что внутри |
|---|---|
status | Числовой HTTP-статус. |
title | Краткая фраза ("Bad Request", "Unprocessable Entity", …). |
detail | Человекочитаемое описание конкретной проблемы. |
instance | Путь запроса. |
traceId | Идентификатор запроса. Скиньте его в поддержку, если нужна помощь — по нему быстро находят запрос в логах. |
code | Стабильный машинный код ошибки (см. ниже). Не меняется между релизами. |
hint | Подсказка, что сделать дальше. |
paramName | Имя параметра, если ошибка относится к одному полю. |
jsonPath | JSON-путь до проблемного узла, если тело сложное. |
validEnumValues | Если поле — enum, и значение вне списка, здесь перечислены допустимые варианты. |
Стабильные значения code:
| code | Что значит |
|---|---|
ValidationFailed | Тело прошло парсер, но не прошло валидацию. |
InvalidArgument | Некорректный параметр (query/route). |
InvalidJson | Тело не парсится как JSON. |
InvalidOperation | Операция логически невозможна в текущем состоянии (например, удалять то, на что есть ссылки). |
WriteRequiresAuthenticatedUser | Эндпоинт требует авторизованного пользователя (внутренний случай, не должен встречаться на Public API). |
WriteRequiresEditorRole | Роль токена недостаточна (на Public API сейчас не встречается — один уровень доступа). |
Canceled | Запрос был отменён клиентом (closed connection). |
InternalError | Неожиданная ошибка сервера. Скиньте traceId в поддержку. |
ProjectSuspended | Проект не активен. |
# Версионирование
API версионируется через URL: …/v1.0/… (runtime) и …/v1/… (management). Внутри одной версии возможны только
аддитивные изменения (новые опциональные поля, новые опциональные параметры, новые опциональные эндпоинты).
Любое ломающее изменение — переименование, изменение типа, удаление поля, изменение семантики — выезжает только
под новой версией (v2/). Старая версия будет жить параллельно, пока есть пользователи; о выводе из эксплуатации
будет анонс заранее.
# Соглашения по форматам
- camelCase во всём, что на проводе (request/response).
- Перечисления (enum) передаются строками:
"Equal","And","Web". Регистр имеет значение. - Идентификаторы — UUID в формате
8-4-4-4-12(например,8f3a1e4b-7c4d-4d5e-9aaf-2c1b6d3e5f01). Server-owned: приPOSTвсегда генерируется сервером, телоidигнорируется. ПриPUTidберётся из URL. - Даты — ISO 8601 в UTC (
2026-04-15T18:23:00Z). - Денежные суммы и метрики в
customUserScopeParams/customPageScopeParamsпередавайте строками ("12350", не12350) — это исторический контракт runtime-эндпоинтов. - Поля, отсутствующие в теле, на
PUTозначают «обнулить / удалить вложенный объект». НаPOSTотсутствующее поле = «значение по умолчанию» / «без вложений». Для частичного обновления используйтеPATCH(там, где он поддерживается).
# Каталог эндпоинтов
# Блок 1. Применение персонализаций и A/B-тестов (runtime)
- Получение персонализаций —
POST /public/{source}/v1.0/personalizations/apply - Получение A/B-тестов —
POST /public/{source}/v1.0/ab-tests/apply
# Блок 2. Передача конверсий
- Передача конверсий —
POST /public/{source}/v1.0/goals/reach
Каталог сторонних платформ. Если вам нужно подключить Яндекс.Метрику / Директ / AppMetrica, AmoCRM, CallTouch, Smartis, UIS, VK Ads, Telegram или AI-агента через MCP — это отдельный раздел документации, Каталог интеграций. Там же — какие сторонние платформы можно использовать как сигнал для ML и как JS-действие в персонализациях.
# Блок 3. Управление сущностями
- Общие правила — пять операций на сущность, единое тело-дерево, server-owned id
- Персонализации
- A/B-тесты
- Опросы
- Rule-based действия
- моделируемые конверсии
- Аудиторные сегменты
- Антиботы
- Smart-фиды
- Условия показа V2 — встраиваются в тело сущности
- Статистика и отчёты — конструктор и запуск отчётов
# Минимальный пример
curl -X GET "https://api.sales-ninja.me/public/api/v1/manage/personalizations?pageSize=5" \
-H "X-SN-TOKEN: 9f5a4c1d-7b8e-4f3a-9d2c-1e7b0a4c5d6f"
const SN_TOKEN = process.env.SN_TOKEN;
const res = await fetch(
'https://api.sales-ninja.me/public/api/v1/manage/personalizations?pageSize=5',
{ headers: { 'X-SN-TOKEN': SN_TOKEN } }
);
if (!res.ok) {
const problem = await res.json();
throw new Error(`${problem.code}: ${problem.detail} (traceId=${problem.traceId})`);
}
const { items } = await res.json();
import os, requests
SN_TOKEN = os.environ["SN_TOKEN"]
r = requests.get(
"https://api.sales-ninja.me/public/api/v1/manage/personalizations",
params={"pageSize": 5},
headers={"X-SN-TOKEN": SN_TOKEN},
timeout=15,
)
if not r.ok:
p = r.json()
raise RuntimeError(f"{p['code']}: {p['detail']} (traceId={p['traceId']})")
items = r.json()["items"]
using var http = new HttpClient { BaseAddress = new Uri("https://api.sales-ninja.me/") };
http.DefaultRequestHeaders.Add("X-SN-TOKEN", Environment.GetEnvironmentVariable("SN_TOKEN"));
using var res = await http.GetAsync("public/api/v1/manage/personalizations?pageSize=5");
res.EnsureSuccessStatusCode();
var body = await res.Content.ReadAsStringAsync();