# Передача конверсий (runtime)
Этот метод позволяет программно сообщить Sales Ninja, что какой-то клиент достиг цели. Применяется в случаях, когда достижение цели происходит не на сайте: в мобильном приложении, в CRM, в backend-сценарии, при импорте исторических данных и т.п.
Функционал — аналог офлайн-конверсий в Яндекс.Метрике (opens new window).
# Описание метода
POST https://api.sales-ninja.me/public/{source}/v1.0/goals/reach
Headers:
X-SN-TOKEN: <ваш токен проекта>
Content-Type: application/json
Параметр {source} — идентификатор внешнего источника (мобильное приложение, серверный сценарий, CRM-импорт и
т.п.). Допустимые значения возвращает поддержка; неизвестный source отдаёт 400.
# Тело запроса
{
"ip": "<IP клиента (опционально)>",
"goalId": "<id цели в Sales Ninja (обязательно)>",
"customerId": "<id клиента в Sales Ninja (обязательно)>",
"sessionId": "<id сессии клиента (опционально)>",
"createdOnUtc": "<время конверсии по UTC (опционально)>",
"customUserScopeParams": { "...": "..." },
"customPageScopeParams": { "...": "..." }
}
# Примеры тела
Полный вариант:
{
"ip": "185.237.80.128",
"goalId": "299837db-9b51-45a4-996b-844d3fd05bbd",
"customerId": "2eccd743-4164-4892-a416-dc5c16f44971",
"sessionId": "123212db-9b51-a416-996b-844d3fd09b51",
"createdOnUtc": "2026-04-15T12:23:25.367Z",
"customUserScopeParams": {
"revenue": "12350",
"netProfit": "900"
},
"customPageScopeParams": {}
}
Минимально достаточный вариант:
{
"goalId": "299837db-9b51-45a4-996b-844d3fd05bbd",
"customerId": "2eccd743-4164-4892-a416-dc5c16f44971",
"customPageScopeParams": {
"revenue": "12350",
"netProfit": "900"
}
}
# Поля
# ip
IP-адрес пользователя, совершившего конверсию. Необязательно, но желательно передавать — он используется для обогащения данных (география, антибот).
# goalId
Уникальный идентификатор цели в системе Sales Ninja (не в вашей CRM/аналитике). Это GUID (opens new window),
например 299837db-9b51-45a4-996b-844d3fd05bbd. Получить его можно из адресной строки редактора цели:
https://app.sales-ninja.me/goal/<id будет тут>/edition
# customerId
Идентификатор клиента в системе Sales Ninja. Получается на сайте через JS:
// Новый асинхронный вариант (рекомендуется)
ninja('getCustomerIdAsync').then(id => {
// id — это customerId
})
// Или в async-контексте
const customerId = await ninja('getCustomerIdAsync')
Если ваша интеграция не на сайте (например, серверный импорт из CRM) — сохраняйте customerId рядом с записью
о клиенте при первой выдаче и используйте его потом при отправке конверсий.
# sessionId
Идентификатор сессии клиента. Тоже из JS:
const sessionId = await ninja('getSessionIdAsync')
# createdOnUtc
Время совершения конверсии в UTC. Если не передано — берётся текущее серверное время.
# customUserScopeParams
Пользовательские параметры клиента в виде объекта ключ-значение. Используются для обогащения данных, в т.ч. для передачи выручки и прибыли.
Значения параметров обязательно строки:
"12350", не12350.
# customPageScopeParams
Пользовательские параметры страницы. Тот же формат, тот же строковый контракт.
# Ответ
При успехе — 200 OK с пустым телом. Конверсия появится в системе через какое-то время (обычно секунды; на
большом потоке могут быть минутные задержки).
При ошибке — стандартный Problem Details с понятным code и
hint.
# Примеры кода
curl -X POST "https://api.sales-ninja.me/public/server/v1.0/goals/reach" \
-H "X-SN-TOKEN: $SN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"goalId": "299837db-9b51-45a4-996b-844d3fd05bbd",
"customerId": "2eccd743-4164-4892-a416-dc5c16f44971",
"createdOnUtc": "2026-04-15T12:23:25Z",
"customUserScopeParams": {"revenue": "12350", "netProfit": "900"}
}'
async function reachGoal({ goalId, customerId, revenue, netProfit }) {
const res = await fetch(
`https://api.sales-ninja.me/public/server/v1.0/goals/reach`,
{
method: 'POST',
headers: {
'X-SN-TOKEN': process.env.SN_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify({
goalId,
customerId,
createdOnUtc: new Date().toISOString(),
customUserScopeParams: {
revenue: String(revenue),
netProfit: String(netProfit),
},
}),
}
);
if (!res.ok) {
throw new Error(`reachGoal failed: ${res.status} ${await res.text()}`);
}
}
import os, requests
from datetime import datetime, timezone
def reach_goal(goal_id: str, customer_id: str, revenue: float, net_profit: float) -> None:
r = requests.post(
"https://api.sales-ninja.me/public/server/v1.0/goals/reach",
headers={"X-SN-TOKEN": os.environ["SN_TOKEN"]},
json={
"goalId": goal_id,
"customerId": customer_id,
"createdOnUtc": datetime.now(timezone.utc).isoformat(),
"customUserScopeParams": {
"revenue": str(revenue),
"netProfit": str(net_profit),
},
},
timeout=15,
)
r.raise_for_status()
# Идемпотентность и дубли
API сейчас не дедуплицирует одинаковые конверсии автоматически. Если ваш интегратор может ретрить запрос —
сохраняйте, какие конверсии уже улетели, и не отправляйте их повторно. Дубликат с тем же customerId/goalId
будет принят и обработан как ещё одна конверсия.