Перейти к содержимому

События с сервера (S2S API)

S2S API (server-to-server, «с сервера на сервер») принимает события от вашего сервера, а не из приложения. Так передают подтверждённую оплату, заказ с сайта или из колл-центра и возврат денег. Tracehub находит установку этого пользователя и засчитывает событие той рекламе, которая привела установку.

S2S API нужен, когда:

  • оплату подтверждает ваш сервер: эквайринг, оплата при получении;
  • покупку сделали вне приложения — на сайте, по телефону, в офисе, — а засчитать её нужно установке из рекламы;
  • нужно передать возврат: SDK отрицательные суммы не отправляет, поэтому возвраты идут только этим путём.

Без ключа сервер не примет запрос, а без тумблера «В атрибуции» покупка не дойдёт до дохода по источникам, поэтому начните с них:

  1. Выпустите ключ S2S API приложения — как это сделать, описано в разделе «Ключ S2S API».
  2. Отправьте первое событие. Его токен сам появится на странице Трекинг → События.
  3. Включите у этого события тумблер В атрибуции. Пока он выключен, покупка не попадёт в доход по источникам и кампаниям. События, которые пришли до включения, не пересчитаются.

Если токен уже отправляет приложение и тумблер у него включён, шаги 2 и 3 не нужны: тумблер общий для событий из приложения и с сервера.

Событие отправляется одним запросом POST с телом в JSON:

Окно терминала
curl -X POST https://s2s.tracehub.ru/s2s/event \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $S2S_API_KEY" \
-d '{
"app_token": "APP_TOKEN",
"event_token": "purchase",
"thid": "THID",
"revenue": 2990.00,
"currency": "RUB",
"deduplication_id": "order-84213",
"created_at": "2026-10-27T12:31:05+03:00",
"os_name": "android"
}'

Замените:

  • $S2S_API_KEY — ключ S2S API приложения. В примере он взят из переменной окружения, чтобы не остаться в истории команд;
  • APP_TOKEN — SDK-токен той платформы, на которой пользователь установил приложение. Он на вкладке SDK страницы приложения: Трекинг → Приложения → ваше приложение;
  • THID — идентификатор устройства, который приложение получило от SDK (см. «thid из SDK»).

Сервер отвечает 202 Accepted:

{ "status": "accepted", "request_id": "6f1c2b9e-4a7d-4c51-9d2e-0b7f3a1c8e55" }

202 значит «принято в обработку», а не «засчитано»: установку Tracehub ищет уже после ответа, и повтор отбрасывает тоже после него. Сохраняйте request_id из ответа — если событие не появится в отчётах, приложите его к обращению в поддержку.

Ключ подтверждает, что запрос пришёл от вашего сервера. Он один на приложение и подходит для всех его платформ — и для Android, и для iOS. Передавайте его в каждом запросе заголовком Authorization: Bearer S2S_API_KEY.

Ключ — на странице приложения:

  1. Откройте Трекинг → Приложения и выберите приложение.
  2. Перейдите на вкладку SDK — карточка Ключ S2S API находится под карточкой HMAC-секрет.
  3. Если ключа ещё нет, нажмите Выпустить ключ. Если есть — нажмите Показать или Копировать.

У нового приложения ключа нет. Выпускать и перевыпускать его может владелец или администратор организации.

Запрос без ключа, с чужим ключом или к приложению, у которого ключ ещё не выпущен, получает ответ 401, и событие не принимается.

Чтобы привязать покупку к установке, Tracehub должен понять, на каком устройстве живёт пользователь. Передайте в запросе хотя бы одно из трёх — способы перечислены от самого надёжного:

Способ Поля Когда подходит
Идентификатор устройства Tracehub thid Заказ оформлен в приложении, и приложение передало thid вашему серверу вместе с заказом
Идентификатор устройства gps_adid, oaid, android_id, idfa, idfv, external_device_id У вашего сервера есть рекламный или собственный идентификатор устройства
Идентификатор пользователя customer_user_id Устройства под рукой нет: заказ с сайта, по телефону, в офисе

Можно передать сразу несколько: Tracehub проверит их по очереди — как именно, описано в разделе «Как Tracehub ищет установку».

thid — идентификатор устройства в Tracehub. SDK получает его с первым ответом сервера, и по нему установка находится напрямую. Прочитайте его в приложении и отправьте своему серверу вместе с заказом:

Tracehub.getThid { thid ->
// null, пока SDK не получил первый ответ сервера
if (thid != null) order.thid = thid
}

order здесь — заказ в вашем приложении, который уходит на ваш сервер. Если заказ оформляют сразу после первого запуска и thid ещё нет, подождите его: на Android — Tracehub.getThidWithTimeout(timeoutMs = 5_000L) { thid -> }, на iOS — await Tracehub.getThidAsync(timeout: 5.0).

Подойдёт любой идентификатор, который SDK присылал с этого устройства: Tracehub хранит их все. Поэтому покупка найдёт установку, даже если ваш сервер знает OAID, а установка пришла с рекламным идентификатором Google.

  • Регистр рекламных идентификаторов не важен.
  • Хеши md5 (gps_adid_md5, idfa_md5, oaid_md5) работают, только если установка пришла с этим же идентификатором: в поиске по всем идентификаторам устройства они не участвуют. Если у вас есть открытое значение, передавайте его.
  • Идентификаторы из одних нулей, значения null и unknown и нераскрытые макросы вроде {gaid} Tracehub пропускает.

customer_user_id — идентификатор пользователя в вашей системе, тот же, что приложение передаёт в SDK. По нему Tracehub находит устройство, с которого SDK последним прислал этот идентификатор. Узнать об этом Tracehub может только от SDK, поэтому приложение должно задать идентификатор заранее:

  1. Если пользователь уже вошёл к моменту запуска SDK, передайте идентификатор в конфигурации. Иначе вызовите setCustomerUserId сразу после входа.
  2. После входа отправьте любое событие SDK, например login. С ним Tracehub сразу свяжет пользователя с устройством, а не при следующем запуске приложения.
  3. При выходе из учётной записи вызовите clearCustomerUserId().
// Пользователь уже вошёл к запуску SDK
val config = TracehubConfig.Builder(this, "APP_TOKEN", Environment.PRODUCTION)
.setCustomerUserId("USER_ID")
.build()
Tracehub.initSdk(config)
// Пользователь вошёл позже — сразу после входа
Tracehub.setCustomerUserId("USER_ID")
Tracehub.trackEvent(TracehubEvent("login"))
// Выход из учётной записи
Tracehub.clearCustomerUserId()

Замените APP_TOKEN на SDK-токен платформы, а USER_ID — на идентификатор пользователя в вашей системе.

Учтите:

  • SDK не сохраняет идентификатор между запусками: задавайте его при каждом старте приложения, как только он известен.
  • В S2S передавайте ровно ту же строку, что в SDK: Tracehub сравнивает значения буква в букву, с учётом регистра и пробелов.
  • Не используйте значения вроде test, null, 0 и строки с символами {, } или %: Tracehub примет их за заглушку или незаполненный макрос и искать по ним не будет.
  • customer_user_id есть в Android SDK с версии 1.1.0 и в iOS SDK с версии 1.0.2.

Подробнее о параметре — на страницах Android SDK: справочник конфигурации и iOS SDK: справочник конфигурации.

Получив событие, Tracehub проверяет идентификаторы из запроса в таком порядке и останавливается на первом, который привёл к установке:

  1. click_id, если он передан, — установка по этому переходу.
  2. thid. Если его нет в запросе, Tracehub вычисляет его из первого подходящего идентификатора в порядке gps_adid, gps_adid_md5, idfa, idfa_md5, oaid, oaid_md5, idfv, android_id — так же, как для запросов SDK. Установка найдётся, если она пришла с этим же идентификатором.
  3. Все идентификаторы устройства из запроса среди тех, что SDK присылал с устройства: gps_adid, idfa, oaid, idfv, android_id, external_device_id, а последним — customer_user_id. Побеждает первое найденное устройство, у которого есть установка.

Если ничего не нашлось, событие всё равно засчитается, но без источника: в отчётах оно попадёт в «Органику».

Несколько правил, которые следуют из этого порядка:

  • Устройство важнее пользователя. Если идентификатор устройства и customer_user_id указывают на разные устройства, событие получит устройство.
  • Поиск идёт только в платформе из app_token. Покупку пользователя iOS-приложения отправляйте с SDK-токеном iOS-платформы.
  • Один пользователь на нескольких устройствах — событие получит устройство, с которого SDK последним прислал его customer_user_id.
  • Повторной проверки нет. Если покупка пришла раньше, чем устройство или пользователь стали известны Tracehub, — например, раньше первого запуска приложения или до первого запроса SDK после входа, — она так и останется без источника.

click_id — идентификатор перехода самого Tracehub (UUID), а не идентификатор клика рекламной сети. Значения вроде yclid или gclid в этом поле Tracehub пропустит и перейдёт к следующему шагу, поэтому в обычной интеграции поле оставляют пустым.

Обязательны только app_token и event_token, но без идентификатора устройства или пользователя событие не к чему привязать.

Поле Тип Что это
app_token строка Обязательное. SDK-токен платформы приложения
event_token строка Обязательное. Токен события — тот же, что отправляет приложение, например purchase
thid строка Идентификатор устройства в Tracehub из SDK
gps_adid, oaid, android_id строка Идентификаторы Android-устройства: рекламный идентификатор Google, OAID, Android ID
idfa, idfv строка Идентификаторы iOS-устройства
gps_adid_md5, idfa_md5, oaid_md5 строка Рекламные идентификаторы в виде md5: GAID и OAID — от записи строчными буквами, IDFA — прописными
external_device_id строка Ваш собственный идентификатор устройства — тот, что приложение задаёт в SDK как externalDeviceId
customer_user_id строка Идентификатор пользователя в вашей системе — тот же, что в SDK
click_id строка Идентификатор перехода Tracehub (UUID)
revenue число Сумма. Со знаком минус — возврат
currency строка Код валюты ISO 4217, например RUB. Без него сумма не попадёт в доход
deduplication_id строка Номер заказа или транзакции — защита от двойного счёта, см. «Повторы и дубли»
created_at строка Время события в формате RFC 3339 с часовым поясом. От него отсчитываются окна атрибуции; без него временем события считается время приёма
os_name строка android или ios — платформа события в отчётах. Без него событие запишется как веб

Ещё несколько полей сервер принимает и сохраняет вместе с событием, но на привязку к установке и на отчёты они не влияют: network_name, callback_params, partner_params, ip_address, user_agent, device_type и environment. Учтите это в трёх случаях:

  • источник события Tracehub берёт из установки, поэтому пометка network_name не перенесёт покупку в отчёт по другой сети;
  • параметры события из callback_params и partner_params не попадут ни в каталог, ни в отчёты. Если значение нужно в отчётах, отправляйте событие из SDK или заложите различие в токен события: purchase_card, purchase_cash;
  • environment: sandbox не отделяет событие от боевых. Для проверок заведите отдельное приложение — см. страницу Доход.

Передавайте deduplication_id в каждом запросе — номер заказа или транзакции. Tracehub отбрасывает событие, если за последние 24 часа уже получал этот номер с тем же токеном события и тем же app_token. Это защищает от двух вещей.

Повтор запроса. Если ваш сервер не дождался ответа и отправил запрос ещё раз, второй экземпляр отбросится, и доход не задвоится.

Одна покупка из приложения и с сервера. Если покупку отправляет и приложение, и ваш сервер, передайте один и тот же номер в обоих: в SDK — как deduplicationId события, в S2S — как deduplication_id, с тем же токеном события и SDK-токеном той же платформы. Засчитается сообщение, которое пришло первым, а второе Tracehub отбросит, даже если сумма в нём другая.

Склейка работает 24 часа от первого сообщения. Если сервер подтверждает покупку позже, чем через сутки после события из приложения, покупка посчитается дважды — в таком случае отправляйте её только одним путём.

Без deduplication_id Tracehub собирает ключ дубля сам — из клика, устройства или пользователя, токена события и created_at. Такой ключ слабее: две покупки одного пользователя за сутки сольются в одну, если у них одинаковое created_at или оно не передано, а при переданном click_id — при любом времени.

Возврат — то же событие дохода, только с отрицательной суммой. Отдельного метода или флага «это возврат» нет.

Окно терминала
curl -X POST https://s2s.tracehub.ru/s2s/event \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $S2S_API_KEY" \
-d '{
"app_token": "APP_TOKEN",
"event_token": "refund",
"thid": "THID",
"revenue": -2990.00,
"currency": "RUB",
"deduplication_id": "order-84213",
"created_at": "2026-10-30T09:14:00+03:00",
"os_name": "android"
}'

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

Условие Почему
Валюта обязательна Без неё сумма не учтётся, и возврат не уменьшит доход
Свой токен события, например refund Иначе возврат не отличить от покупки, а с тем же номером заказа он отбросится как её повтор
У токена возврата включён тумблер В атрибуции Иначе возврат не уменьшит доход в отчётах по источникам

Возврат уменьшает чистый доход, а счётчик транзакций от него не убывает — подробнее в разделе «Возвраты» на странице Доход.

Код Что случилось Что делать
202 Событие принято в обработку Ничего
400 Нет app_token или event_token, либо тело запроса — не JSON Исправьте запрос: повтор без исправления получит тот же ответ
401 Нет ключа, ключ не подходит этому приложению или у приложения ещё нет ключа Проверьте заголовок Authorization и ключ на вкладке SDK карточки приложения
415 Нет заголовка Content-Type: application/json Добавьте заголовок
422 Поле не того типа — например, сумма строкой "2990", а не числом Исправьте тип поля
429 Слишком много запросов Повторите позже
503 Сервер временно не принимает события Повторите позже с тем же deduplication_id

Причину ошибки сервер пишет в тело ответа. У каждого ответа, и успешного, и с ошибкой, есть заголовок X-Request-Id — его тоже можно приложить к обращению в поддержку.

На странице Трекинг → События событие, которое приходит с сервера, отмечено меткой Офлайн-конверсия. Фильтр Источник оставит в списке только такие события или, наоборот, только события из приложения. Если токен приходит и оттуда, и оттуда, подсказка у метки напомнит передавать в обоих случаях один номер заказа.