События с сервера (S2S API)
This content is not available in your language yet.
S2S API (server-to-server, «с сервера на сервер») принимает события от вашего сервера, а не из приложения. Так передают подтверждённую оплату, заказ с сайта или из колл-центра и возврат денег. Tracehub находит установку этого пользователя и засчитывает событие той рекламе, которая привела установку.
S2S API нужен, когда:
- оплату подтверждает ваш сервер: эквайринг, оплата при получении;
- покупку сделали вне приложения — на сайте, по телефону, в офисе, — а засчитать её нужно установке из рекламы;
- нужно передать возврат: SDK отрицательные суммы не отправляет, поэтому возвраты идут только этим путём.
Перед первым запросом
Заголовок раздела «Перед первым запросом»Без ключа сервер не примет запрос, а без тумблера «В атрибуции» покупка не дойдёт до дохода по источникам, поэтому начните с них:
- Выпустите ключ S2S API приложения — как это сделать, описано в разделе «Ключ S2S API».
- Отправьте первое событие. Его токен сам появится на странице Трекинг → События.
- Включите у этого события тумблер В атрибуции. Пока он выключен, покупка не попадёт в доход по источникам и кампаниям. События, которые пришли до включения, не пересчитаются.
Если токен уже отправляет приложение и тумблер у него включён, шаги 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 из ответа — если событие не появится в отчётах, приложите его к обращению в поддержку.
Ключ S2S API
Заголовок раздела «Ключ S2S API»Ключ подтверждает, что запрос пришёл от вашего сервера. Он один на приложение и подходит для всех его платформ — и для Android, и для iOS. Передавайте его в каждом запросе заголовком Authorization: Bearer S2S_API_KEY.
Ключ — на странице приложения:
- Откройте Трекинг → Приложения и выберите приложение.
- Перейдите на вкладку SDK — карточка Ключ S2S API находится под карточкой HMAC-секрет.
- Если ключа ещё нет, нажмите Выпустить ключ. Если есть — нажмите Показать или Копировать.
У нового приложения ключа нет. Выпускать и перевыпускать его может владелец или администратор организации.
Запрос без ключа, с чужим ключом или к приложению, у которого ключ ещё не выпущен, получает ответ 401, и событие не принимается.
Как указать пользователя
Заголовок раздела «Как указать пользователя»Чтобы привязать покупку к установке, Tracehub должен понять, на каком устройстве живёт пользователь. Передайте в запросе хотя бы одно из трёх — способы перечислены от самого надёжного:
| Способ | Поля | Когда подходит |
|---|---|---|
| Идентификатор устройства Tracehub | thid |
Заказ оформлен в приложении, и приложение передало thid вашему серверу вместе с заказом |
| Идентификатор устройства | gps_adid, oaid, android_id, idfa, idfv, external_device_id |
У вашего сервера есть рекламный или собственный идентификатор устройства |
| Идентификатор пользователя | customer_user_id |
Устройства под рукой нет: заказ с сайта, по телефону, в офисе |
Можно передать сразу несколько: Tracehub проверит их по очереди — как именно, описано в разделе «Как Tracehub ищет установку».
thid из SDK
Заголовок раздела «thid из SDK»thid — идентификатор устройства в Tracehub. SDK получает его с первым ответом сервера, и по нему установка находится напрямую. Прочитайте его в приложении и отправьте своему серверу вместе с заказом:
Tracehub.getThid { thid -> // null, пока SDK не получил первый ответ сервера if (thid != null) order.thid = thid}// nil, пока SDK не получил первый ответ сервераif let thid = Tracehub.getThid() { 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
Заголовок раздела «customer_user_id»customer_user_id — идентификатор пользователя в вашей системе, тот же, что приложение передаёт в SDK. По нему Tracehub находит устройство, с которого SDK последним прислал этот идентификатор. Узнать об этом Tracehub может только от SDK, поэтому приложение должно задать идентификатор заранее:
- Если пользователь уже вошёл к моменту запуска SDK, передайте идентификатор в конфигурации. Иначе вызовите
setCustomerUserIdсразу после входа. - После входа отправьте любое событие SDK, например
login. С ним Tracehub сразу свяжет пользователя с устройством, а не при следующем запуске приложения. - При выходе из учётной записи вызовите
clearCustomerUserId().
// Пользователь уже вошёл к запуску SDKval 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()// Пользователь уже вошёл к запуску SDKlet config = TracehubConfig(appToken: "APP_TOKEN", environment: .production)config.customerUserId = "USER_ID"Tracehub.initSdk(config: config)
// Пользователь вошёл позже — сразу после входаTracehub.setCustomerUserId("USER_ID")Tracehub.trackEvent(TracehubEvent(eventToken: "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 ищет установку
Заголовок раздела «Как Tracehub ищет установку»Получив событие, Tracehub проверяет идентификаторы из запроса в таком порядке и останавливается на первом, который привёл к установке:
click_id, если он передан, — установка по этому переходу.thid. Если его нет в запросе, Tracehub вычисляет его из первого подходящего идентификатора в порядкеgps_adid,gps_adid_md5,idfa,idfa_md5,oaid,oaid_md5,idfv,android_id— так же, как для запросов SDK. Установка найдётся, если она пришла с этим же идентификатором.- Все идентификаторы устройства из запроса среди тех, что 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 — его тоже можно приложить к обращению в поддержку.
Офлайн-конверсии в кабинете
Заголовок раздела «Офлайн-конверсии в кабинете»На странице Трекинг → События событие, которое приходит с сервера, отмечено меткой Офлайн-конверсия. Фильтр Источник оставит в списке только такие события или, наоборот, только события из приложения. Если токен приходит и оттуда, и оттуда, подсказка у метки напомнит передавать в обоих случаях один номер заказа.
- Доход — как Tracehub пересчитывает валюты и раскладывает доход на валовой, возвраты и чистый.
- События и параметры — каталог событий и тумблер «В атрибуции».
- Android SDK: справочник конфигурации и iOS SDK: справочник конфигурации —
customerUserIdи чтениеthidв приложении.

