INOTrackбыстрота, надежность и ахуенность

Постбеки: приём конверсий от партнёрских сетей

Защита от «нулевого платежа без сигнала»

Если в маппинге эндпоинта не указан параметр суммы или сеть присылает деньги под именем, которого маппинг не ждёт, конверсия с выплатой 0 при ответе сети 200 OK осталась бы незамеченной до сверки с личным кабинетом сети. Поэтому на каждый постбек с итоговой суммой 0 (уже ПОСЛЕ того, как найден клик и известен его оффер) проверки идут по приоритету — от самого надёжного сигнала к самому неявному:

  1. При создании/изменении маппинга пустое поле sum не даёт сохраниться без явного флага подтверждения (query-параметр allow_no_sum=1) — обычное сохранение через форму/API с пустым sum вернёт ошибку валидации.
  2. Деньги видны в другом параметре запроса. Если прочитанный маппингом параметр суммы пришёл нулём (payout=0.00), а рядом лежит непрочитанный параметр, похожий на сумму (по вхождению подстроки: sum, amount, payout, revenue, payment, price, value, commission, reward, profit, income, cost) с ненулевым значением (amount=12.50) — конверсия уходит в карантин unread_sum_param с перечнем этих параметров. Свою цифру за оператора не выбираем — обе интерпретации могут быть верны, это ручной разбор.
  3. Параметр суммы не настроен/отсутствует в запросе, но похожий на деньги параметр есть. Тот же карантин unread_sum_param — сеть, похоже, прислала выплату под именем, не подключённым в маппинге.
  4. Ничего похожего на сумму в запросе нет вообще. С точки зрения кода это неотличимо от «у события действительно нет выплаты» (например, lead без депозита) и от «сеть назвала параметр словом, которого нет в списке хинтов выше» — это и есть тот самый редкий случай, который раньше проходил молча. Теперь выбираются условия по ГЕО клика, а при отсутствии строки страны — общие условия offers.*. Фиксированная часть cpa/cpl/hybrid подставляется только за указанную в условиях цель и при ненулевом payout; валюта берётся из тех же условий (при пустой — из оффера). У общих условий цели нет, поэтому сохраняется прежнее правило: cpl платит за lead, cpa — за approve или ftd, общий hybrid ничего не подставляет. Конверсия помечается _sum_source=offer_default (видно в raw), публикуется алерт postback_sum_offer_default и растёт метрика inotrack_postback_sum_offer_default_total. Подстановка заметна и требует проверить маппинг сети. revshare ничего не подставляет: процент справочный, фактический доход приходит отдельными постбеками rebill/commission. У hybrid подставляется только фиксированная часть за её цель; процент от депозита трекер не вычисляет.
  5. Последний рубеж, независимо от пп. 2–4: подтверждённая конверсия с нулевой выплатой при ненулевой фиксированной части действующих условий за эту цель вызывает алерт postback_approved_zero_payout и рост метрики inotrack_postback_approved_zero_payout_total. Ноль на revshare и на событиях гибрида вне цели фиксированной части не считается дефектом. Алерты содержат ГЕО клика и действующую модель ставки. Запись не блокируется: сеть может быть права насчёт нуля, но случай становится видимым сразу. Сумма, присланная сетью, всегда главнее справочных условий.

Что до сих пор не покрыто: подстановка offer_default (п. 4) — это справочная выплата оффера, а не то число, которое реально прислала сеть за КОНКРЕТНУЮ конверсию (для CPA с фиксированной ставкой это обычно одно и то же, но не гарантированно — например, если сеть платит по факту меньше из-за собственных корректировок). Для revshare и переменной части hybrid без явного денежного параметра ноль пишется без подстановки: угадывать доход игрока нельзя. Проверяйте вручную: после подключения новой сети отправьте тестовый постбек (см. ниже) и сверьте сумму в ответе с тем, что реально прислала сеть.

Формат входящей ссылки

GET/POST https://<трекер-домен>/pb/{token}?clickid={clickid}&status=approve&sum=25&txid=123
  • {token} — свой токен на каждый postback-эндпоинт (не путать с токеном потока). Один эндпоинт = один физически подключённый провод от конкретной сети.
  • Метод — GET или POST (в т.ч. application/x-www-form-urlencoded в теле). Если один и тот же параметр пришёл и в query, и в теле — побеждает тело.
  • Имена параметров (clickid, status, sum, txid...) настраиваются в маппинге эндпоинта под конкретную сеть — это не фиксированный список, а то, что вы укажете.
  • Опционально — allowlist IP/CIDR для эндпоинта: если задан, запросы не из списка получают 403.

Ответ почти всегда 200 OK, даже если постбек в итоге уйдёт в карантин или будет подавлен дедупом — это осознанное решение: карантин и дедуп — наши внутренние состояния, а не повод запускать у сети ретрай-шторм по уже принятому запросу.

⚠️ Важно про момент ответа: /pb/{token} — быстрый синхронный приёмник. Он делает только офлайн-проверку (парсится ли маппинг, есть ли clickid) и сразу кладёт постбек в персистентную очередь Postgres, отвечая сети 200 accepted — тело "accepted". Собственно обработка (дедуп, поиск клика, проверка суммы на карантин, запись конверсии) выполняется асинхронно фоновым воркером (опрос очереди каждые 500 мс). Поэтому в теле немедленного ответа НЕ будет буквального duplicate или quarantined: click_not_found — это исходы, которые нужно смотреть в списке карантина (GET /api/internal/postback/quarantine, либо через прокси GET /api/v1/postback/quarantine) или в логе очереди приёма, а не в ответе на сам HTTP-запрос. Синхронно в ответе видны только quarantined: no_clickid, quarantined: mapping_error и quarantined: bad_sum — это единственные проверки, которые не требуют похода в БД.

Отсутствие клика при этом не приводит к карантину немедленно: если клик ещё не успел доехать из буфера клик-пути в ClickHouse, воркер ретраит с бэкоффом (до 20 попыток) и уводит в карантин click_not_found только на последней — то есть окончательный статус постбека может стать известен через несколько секунд после ответа 200 accepted, не в момент самого запроса.

Статусная модель конверсии

Порядок стадий воронки: reg → lead → approve/reject → ftd → dep → rebill (approve и reject — одна и та же стадия принятия решения по лиду).

Каждый постбек с новым статусом обновляет ту же конверсию, а не создаёт дубль — история изменений статуса пишется отдельно (кто, когда, старый статус → новый). Если новый статус «откатывается назад» по порядку воронки (например, пришёл lead после ftd) — запись не блокируется (сеть — источник истины по своим данным), но это помечается как нетипичный переход, полезно при отладке маппинга новой сети.

Отдельно от goal (reg/lead/approve/...) хранится свёрнутый трёхзначный статус: pending / approved (approve, ftd, dep, rebill) / rejected — им пользуются отчёты, которым нужна «подтверждённая выручка» независимо от конкретного шага воронки.

Дедупликация

Идемпотентность — по комбинации (сеть, clickid, goal, txid): при повторном поступлении того же набора конверсия не пишется повторно. Сама проверка дедупа — часть асинхронной обработки (см. предупреждение выше), поэтому HTTP-ответ на дублирующий запрос всё равно 200 accepted, а не 200 duplicate — распознать дубль по немедленному ответу нельзя, только по логу обработки. Ключ дедупа живёт в Redis 30 дней. Если сеть присылает повторные постбеки по одному и тому же лиду с ростом статуса (lead → approve → ftd), каждый новый goal — это новая точка дедупа, старые статусы не блокируют новые.

Постбек без клика / без clickid

  • Нет параметра clickid вообще → карантин no_clickid.
  • Есть clickid, но клика с таким ID нет в базе (устарел, опечатка на стороне сети) → карантин click_not_found. Если клик просто ещё не дописался в ClickHouse — это не карантин с первой попытки: фоновый воркер ретраит с бэкоффом (см. предупреждение выше про асинхронную обработку) и уводит в карантин только когда попытки исчерпаны.
  • Параметр статуса не сопоставлен ни одному значению в словаре маппинга и не похож на наши стандартные значения (reg/lead/approve/reject/ftd/dep/rebill) → карантин mapping_error.
  • Сумма пришла, но это не число (отрицательная, нечисловая или неоднозначная вроде 1,234) → карантин bad_sum.
  • Сумма пришла, но не читается маппингом (см. раздел выше) → карантин unread_sum_param.

Карантинные постбеки не теряются: они складываются в отдельную таблицу с исходными параметрами запроса, и оттуда конверсию можно доматчить руками на нужный clickid.

Окна атрибуции

На каждом оффере настраивается срок жизни клика для привязки конверсии (attribution_hours) и модель — last_click (по умолчанию) или first_click. Если attribution_hours не передан или равен 0 в POST /api/v1/offers, оффер получает значение по умолчанию — 720 часов (30 дней). Конверсия, пришедшая позже окна, всё равно записывается, но с пометкой «вне окна атрибуции» и в атрибутированные не попадает.

Тестовый постбек

Есть отдельный dry-run эндпоинт, который прогоняет маппинг и все проверки (включая поиск клика и детект «сумма не читается»), но не делает ни одной записи — удобно проверить новый маппинг сети перед тем, как отдавать ей боевую ссылку.

Пресеты сетей

Готовые (стартовые) маппинги параметров и словари статусов есть для: Leadgid, Pampadu, Unicom24, Guruleads, Leads.su, PIN-UP Partners, 3snet, Vavada, RevenueLab, Zeydoo, RakeCore/Rollix, AppMetrica, CityAds, Admitad — плюс custom для произвольного маппинга. Все они есть в выпадающем списке формы подключения в панели. Подключения, заведённые раньше, пресет не меняет: у каждого свой сохранённый маппинг.

⚠️ Важно: имена параметров и значения статусов в пресетах — типовые для рынка CPA-сетей на момент написания, но не сверены построчно с актуальной документацией каждой конкретной сети. Пресет — это отправная точка для заполнения формы, а не гарантированно рабочая интеграция «из коробки». При подключении сети маппинг нужно свериться по её актуальной документации и при необходимости поправить (маппинг эндпоинта полностью переопределяет любое поле пресета).

Пример полей пресета (PIN-UP Partners): clickid → clickid, status → status, sum → sum, txid → txid, словарь статусов registration/reg → reg, qualified → lead, ftd → ftd, deposit/repeated_dep → dep, валюта по умолчанию USD. Полный список полей и словарей для остальных пресетов — в ответе GET /api/v1/postback/presets.

Сверка с документацией сетей 04.10.2026:

  • Leadgid — сверен по справке переменных на экране «Постбеки» кабинета вебмастера my.leadgid.com (открытого help-центра нет: help.leadgid.com закрыт). Наш clickid — в aff_sub ссылки оффера («SubID 1 указанный вами в трекинг ссылке»), постбек возвращает его {aff_sub}. Статусы {status}: pending → lead, approved → approve, rejected → reject (старые ключи lead/hold/trash пресета оставлены). Сумма {payout}, валюта {currency} (без неё — RUB), txid {lead_id} (один на все смены статуса лида), внешний id оффера {offer_id}, цель {goal_id} — в доп. поле goal_id. Переход pending → rejected Leadgid постбеком не шлёт (так написано в кабинете) — такие отказы видны только в сверке со статистикой сети. Открытой документации API нет (Swagger — только в кабинете), отдельного адаптера сверки нет.
  • Admitad — сверен по статье «Postback URL» базы знаний Admitad (https://admitad.useresponse.com/knowledge-base/article/postback-url_5). Постбек настраивается в кабинете: Tools → Postback URL, событие «Action». Макросы — в тройных квадратных скобках. Наш clickid — в subid4 партнёрской ссылки (…/g/…/?subid4={clickid}): Admitad отводит subid4 под click id и transaction id, а subid…subid3 остаются под метки источника. Постбек возвращает его [[[subid4]]]. Статус [[[payment_status]]]: new → lead, approved → approve, declined → reject, pending (подтверждённое действие вернули в холд) → lead. Сумма [[[payment_sum]]] (заработок вебмастера), валюта [[[currency]]] (валюта программы; без неё — RUB), txid [[[action_id]]] (один на все смены статуса действия), внешний id оффера [[[offer_id]]]. В доп. поля уходят [[[order_id]]], [[[order_sum]]], [[[type]]] (lead/sale) и [[[reward_ready]]] — признак «готово к выводу»; на цель он не влияет. Имена параметров пресета совпадают со стандартными именами Admitad, поэтому подходит и «Simple mode» кабинета (полный набор параметров), и «Advanced mode».
  • CityAds — сверен по разделу «Conversion Postback» документации вебмастера (http://userdocs.cityads.com/docs/udocs/en/latest/content/tools/ConversionPostback.html). Макросы — в фигурных скобках. Наш clickid — в xid ссылки, постбек возвращает его {xid}. Статус {status}: open → lead, approved → approve, rejected → reject. Сумма {payout} (комиссия вебмастера, разделитель — точка), валюта {payout_currency} (валюта аккаунта вебмастера; в примере документации приходит RUR — приём считает его рублём), txid {cpl_id} (один у лида и продажи одного заказа), внешний id оффера {offer_id}. В доп. поля уходят {action_type} (CPL/CPA) и {order_amount}. Сумма, валюта и статус заполняются только в постбеке продажи (CPS); в постбеке лида (CPL) сумма и валюта пустые — конверсия ляжет лидом без денег. Постбек создаётся в кабинете (Tools → Conversion Postback) и проходит модерацию CityAds.
  • Guruleads — открытой документации по ссылке, макросам и статусам не найдено (справка по постбекам только в кабинете). Пресет guruleads — прежняя отправная точка, не сверен.
  • Saleads — не найдено: сеть принимает в ссылке любые метки (saleads.pro/tools, «Субаккаунты»), но макросы постбека и статусы подсказываются только в кабинете («Инструменты → Глобальный постбек», отдельная ссылка на каждое событие). Пресета нет — подключение своим маппингом (custom), статус в каждой из ссылок кабинета задаётся вручную.

Pampadu, Unicom24 и Leadgid: готовая ссылка для кабинета сети

Для этих сетей есть справочник макросов (GET /api/internal/postback/networks). Кнопка «Для сети» в списке подключений собирает строку по фактическому маппингу подключения (GET .../endpoints/{id}/network-template). Для Leadgid строку пока отдаёт только API: в панели кнопка показывается для Pampadu и Unicom24, ссылку Leadgid собрать по таблице ниже:

Сеть Где в кабинете Ссылка
Pampadu Вебмастеру → Postback, GET https://<домен>/pb/<token>?clickid={sub1}&status={status}&sum={amount}&order_id={order_id}
Leadgid Инструменты → Постбеки → Глобальный → Создать, GET, события «Новая конверсия» и «Обновление статуса» https://<домен>/pb/<token>?clickid={aff_sub}&status={status}&payout={payout}&lead_id={lead_id}&offer_id={offer_id}&currency={currency}&goal_id={goal_id}
Unicom24 Инструменты → Postback → Создать, «Глобальный», GET, статусы «В работе», «Подтвержден», «Отклонен», «Оплачено» https://<домен>/pb/<token>?click_id={click_id}&conv_status={conv_status}&payment_sum={payment_sum}&redirect_id={redirect_id}&offer_id={offer_id}&is_paid={is_paid}&sub_id1={sub_id1}&status={status}

Статусы: Pampadu Pending, Check → lead (холд), Approved → approve, Declined → reject — сверено 22.10.2026 с подсказками авторизованной формы «Новый постбэк»; clickid в {sub1} и order_id (id заявки, единица дедупа — им, а не суммой/статусом/offer_name) из той же справки тоже подтверждены. Unicom24 conv_status: pending → lead, approved → approve, declined → reject. Статус перехода {status} (receive/accept/…) и {is_paid} пишутся в доп. поля конверсии. Id заявки в переменных Unicom24 нет, поэтому txid — хеш перехода {redirect_id}: он один на все смены статуса заявки.

Pampadu: проверено 22.10.2026 в авторизованной форме «Новый постбэк», без сохранения и отправки теста. Сумма — {amount}, Gross — {cost}; {click_id} — идентификатор клика Пампаду, а clickid трекера возвращается через {sub1}. Валюта и фактическая выплата по реальной конверсии не проверены: отчёт за 7 дней пуст.

Переменная Смысл в форме
{offer_id} Уникальный идентификатор оффера
{offer_name} Наименование оффера
{click_id} Уникальный идентификатор перехода на стороне Пампаду
{order_id} Уникальный идентификатор конверсии
{status} Статус конверсии
{conversion_time} Время получения конверсии (подсказка; подпись строки — «Статус конверсии»)
{amount} Сумма по конверсии
{cost} Сумма Gross
{promo} Промокод
{goal_id} ID цели
{goal_name} Имя цели
{deposit} Количество депозитов
{sub1} Переданный SubID 1 (clickid трекера)
{sub2} Переданный SubID 2, кнопка «Добавить sub»
{sub3} Переданный SubID 3, кнопка «Добавить sub»
{sub4} Переданный SubID 4, кнопка «Добавить sub»
{sub5} Переданный SubID 5, кнопка «Добавить sub»

Сверка с отчётом сети и дозаливка

Публичного API статистики у Pampadu и Unicom24 нет: обеим сетям конверсии сверяются либо загруженным файлом (ниже), либо (если сеть подключена источником «Кабинет (веб)» — см. подраздел «Кабинет (веб)» дальше) — построчным отчётом, который трекер сам забирает из кабинета партнёра. Вкладка «Сверка с сетью» (только admin) принимает выгрузку конверсий из кабинета: CSV (;, , или табуляция, UTF-8 или Windows-1251) или XLSX, до 10 МБ и 50 000 строк. Колонки сопоставляются с полями clickid, статус, ID заявки, вознаграждение, дата, ID офера и валюта. Маппинг подбирается по заголовкам и сохраняется на подключении.

Каждая строка сравнивается с действующим решением по (clickid, txid) в ClickHouse за окно до 30 дней. Возможные итоги: missing (конверсии у нас нет), status_behind (у нас лид, у сети решение), status_conflict, payout_diff, ours_ahead, ours_only, ok, skipped (нет clickid, строка старше окна, клика нет, статус не распознан, повтор строки). Если ни один ID заявки из файла по клику не совпал ни с одной нашей транзакцией этого клика, строка получает status_conflict, а не missing: скорее всего колонкой «ID заявки» выбран не тот идентификатор, что приходит постбеком, и дозаливка задвоила бы конверсию (у Белки ключ it:{clickid}:{order_id}). Отставший статус дозаливается в ту же транзакцию, с txid нашего решения. «Проверить» только строит отчёт. «Дозалить» ставит missing и status_behind в обычную очередь приёма постбеков с параметрами в именах маппинга подключения. Дальше они идут обычным путём: словарь статусов, дедуп, антифрод, курс, запись, вебхук conversion. Отличия дозаливки:

  • конверсия датируется датой из файла, обрезанной в [клик, сейчас];
  • капы её не считают;
  • исходящие постбеки в источники трафика по умолчанию не уходят (переключатель в форме);
  • в событии и вебхуке conversion есть поле Origin: "reconcile". Остальные поля не менялись.

Служебные параметры _it_* в строке очереди ставит только сверка. Из запросов сетей на /pb они вырезаются. Повторная загрузка того же файла ничего не дублирует. Прогоны и их строки хранятся в postback_reconcile_runs, агрегат пишется в network_reconcile для сверки финансов.

Сверка по API: Admitad, CityAds, Leads.su

У сетей с API конверсий сверка идёт сама, без файла. Включается в настройках подключения сети (источник «API»), поле «Сверка конверсий по API» (config.api_reconcile). По умолчанию стоит report. Планировщик интеграций раз в api_reconcile_interval_min минут (по умолчанию 60, от 15 до 1440) забирает конверсии сети за последние api_reconcile_days дней (по умолчанию 7, не больше 30). Дальше строки идут в ту же сверку, что у файла, с подключением постбеков этой сети у рекламодателя: пресет cityads, leads_su, admitad или config.reconcile_preset (подключение Admitad, заведённое до появления пресета, обычно custom — его и укажите).

Сеть Метод API clickid ID конверсии (txid)
Admitad GET /statistics/actions/ метка clickid_param; если не задана — subid, а при пустом subid — subid4 action_id
CityAds GET …/xml/orderstatistics/{from}/{to}, по дате конверсии; асинхронный отчёт (204 + request_id) дочитывается xid submissionID; config.reconcile_txid = order берёт orderID, none отключает
Leads.su GET conversions, все статусы метка clickid_param (по умолчанию aff_sub1) id; config.reconcile_txid = none отключает

Статусы адаптер переводит в pending/approved/rejected. Словарь пресета превращает их в значения макроса постбека, например open у CityAds или accepted у Leads.su. Режимы:

  • report: только отчёт в истории сверок подключения, с подписью «сверка API · <сеть>»;
  • backfill: отчёт плюс дозапись. Потерянный постбек (missing) и отставший у нас статус (status_behind) ставятся в очередь приёма с Origin: "reconcile", так же, как кнопка «Дозалить». Включается только после отчёта с совпадением txid, см. ниже;
  • off: сверка выключена.

Повторный прогон ничего не дублирует. Поздний постбек с тем же txid отсекает дедуп (сеть, clickid, goal, txid). Конверсии моложе api_reconcile_grace_min минут (по умолчанию 120) ждут следующего прогона, потому что их постбек мог ещё не дойти. Агрегат за период пишется в network_reconcile, это раздел «Финансы → Сверка».

Ссылка оффера Admitad из каталога — gotolink программы с subid4={clickid} (config.clickid_param меняет метку: subid, subid1…subid4). Ссылку, которую оператор правил руками, импорт не трогает — например subid={clickid} у прежних подключений остаётся, и сверка по-прежнему читает clickid из subid. Если subid занят своей меткой источника, а clickid идёт в subid4, задайте clickid_param = subid4 явно. Генератор deeplink Admitad (ссылка на конкретную страницу магазина, admitad.Client.Deeplink) в импорт каталога не подключён: каталог не знает целевую страницу, а запрос на каждую программу при каждой синхронизации — лишняя нагрузка на API. Ссылку на страницу пока берут в кабинете Admitad (генератор Deeplink) и вставляют в оффер с subid4={clickid}.

Каждый прогон записывается в журнал загрузок интеграции (тип stats). Там видно число строк, отложенных свежих строк и итог сверки.

Совпадение txid и включение дозаписи

Дозапись кладёт конверсию с txid из API сети. Если это не тот id, что сеть шлёт в постбеке, поздний постбек не попадёт в дедуп и ляжет второй конверсией с выплатой. Документация CityAds не говорит, какой id статистики совпадает с {cpl_id} постбека. Leads.su может не передавать id конверсии в постбеке вовсе. Поэтому каждый прогон считает долю совпадения txid:

  • M: строки сети, по click id которых у нас есть конверсия;
  • N: из них строки, чей txid равен txid нашей конверсии этого клика.

Панель показывает у интеграции «совпадение txid: N из M (X %)»: на странице импорта и в форме подключения рядом с режимом сверки. В журнале прогона это поля txid_compared, txid_matched, txid_alt (с каким полем id из API совпал наш txid), txid_advice. Вторая конверсия клика с потерянным постбеком расхождением не считается, если остальные конверсии клика сошлись.

Включить backfill в панели и по API (PUT /integrations/{id}) можно, только если последний прогон в режиме report не старше 7 дней показал совпадение от 95 % при M от 20 и с тех пор не менялись reconcile_txid и reconcile_preset. Иначе ядро отвечает 409 с причиной и цифрами. Смена reconcile_txid или reconcile_preset при включённой дозаписи проверяется так же. Проверить заранее: GET /integrations/{id}/reconcile-check.

Администратор может включить дозапись без подтверждения: флажок «Я понимаю риск дублей» в форме, в API поле accept_duplicate_risk: true. Такое включение пишется в audit_log с действием integration.api_reconcile_backfill_forced и текстом отказа. Обычное включение по отчёту пишется с действием integration.api_reconcile_backfill.

Маппинг сверка сама не меняет. При низком совпадении отчёт подсказывает, что сделать:

Что видно в отчёте Что сделать
CityAds: txid постбека совпадает с orderID, а не с submissionID reconcile_txid = order, дождаться нового отчёта
Наши конверсии без txid: постбек не передаёт id конверсии добавить макрос id в ссылку постбека ({cpl_id} у CityAds, id конверсии в action_id у Leads.su) либо reconcile_txid = none
Не совпало ни одно поле id из API проверить, какой макрос стоит в параметре txid ссылки постбека; дозапись не включать

Кабинет (веб): Unicom24, Pampadu

У сетей без публичного API есть источник подключения «Кабинет (веб)» (config.source = "web" на интеграции): вместо ручной выгрузки файлом трекер сам входит в кабинет партнёра под реквизитами оператора и внутренним JSON-API кабинета тянет и каталог офферов, и построчный детальный отчёт конверсий — тот же формат, что у сверки файлом выше, только без файла.

Реквизиты входа задаются в панели, на подключении интеграции сети, и хранятся зашифрованными (network_web_sessions, тем же ключом, что и секреты интеграций, INOTRACK_INTEG_ENC_KEY) — наружу отдаётся только статус сессии (есть/нет, вход логином, cookie или токеном, до когда действует, когда проверена последний раз), сами пароль, cookie и токен по API не читаются:

Сеть Поля формы
Unicom24 логин и пароль партнёра или cookie сессии кабинета
Pampadu pampadu_login + pampadu_password или pampadu_session_token (значение usrtoken из localStorage браузера, без префикса Bearer)

Каталог офферов в режиме «Кабинет (веб)» синхронизируется по расписанию config.sync_interval_min (15–60 минут, по умолчанию 30 — чаще, чем у источника «API»).

Построчная сверка конверсий (раздел выше) по кабинету — режим config.web_reconcile:

  • off — не сверять;
  • report (по умолчанию) — только отчёт, без записи: первый прогон стоит посмотреть глазами, прежде чем доверять ему дозапись денег;
  • backfill — отчёт плюс дозаливка потерянных (missing) и отставших (status_behind) конверсий в обычную очередь приёма постбеков — так же, как кнопка «Применить» у сверки загруженным файлом.

Для Pampadu дозаливка из кабинета отключена: backfill всегда выполняется как report, без создания конверсий. 22.10.2026 живое чтение подтвердило каталог (542 оффера, 414 доступны аккаунту) и пустой отчёт за 7 дней. Идентификаторы и суммы непустых строк отчёта ещё не проверены. Процентные ставки хранятся в примечании как revshare, без подстановки процента в фиксированную выплату RUB. CSV/XLSX остаётся запасным путём.

Период прогона — config.web_reconcile_interval_min (15–1440 минут, по умолчанию 60): детальный отчёт кабинета за 30 дней — не лёгкий запрос, чаще 15 минут его дёргать смысла нет. Строки сверяются с решением по (clickid, txid) тем же ReconcileRows, что и у файла; подключение постбеков сети у рекламодателя для сверки ищется по пресету (unicom24/pampadu). Повторный прогон ничего не дублирует.

Режим «Кабинет (веб)» заменяет собой ручную загрузку файла, а не добавляет официальный API сети — публичного API статистики у Pampadu и Unicom24 как не было, так и нет.

Антифрод конверсий

Всплеск конверсий считается в Redis ZSET по подсети и отпечатку устройства: точное скользящее окно с включёнными границами, без текущего постбека. Повтор сети/clickid/goal/txid не увеличивает счётчик; тестовые конверсии исключены. При недоступном или пустом Redis burst временно не срабатывает, приём продолжается. CR — конверсии из conversions FINAL / сумма кликов из stats_hourly без FINAL. Окно знаменателя округлено до часа; реплей WAL может увеличить сумму MV. Оба эффекта снижают CR и ложные срабатывания; тестовые клики исключены MV.

Каждая входящая конверсия проверяется на признаки фрода и, при срабатывании, помечается флагом suspicious с причиной (в отдельном отчёте, не исключается из базы автоматически — решение остаётся за оператором):

  • конверсия пришла без соответствующего клика в базе;
  • аномально быстрый переход клик → конверсия;
  • всплеск конверсий с одной подсети/устройства;
  • CR источника выше правдоподобного порога;
  • конверсия по клику, который бот-модуль пометил как бота.

Алерт «Потерянные постбеки»

Сверка с сетью в режиме дозаливки исправляет цифры, но с опозданием на свой интервал, и ничего не говорит о причине. Если сверка раз за разом дозаписывает конверсии, значит постбек этой сети не доходит: ссылка в кабинете сети выключена, указывает на старый домен или отвечает ошибкой.

После каждого прогона сверки, запущенного интеграцией, — по API (Admitad, CityAds, Leads.su) или по кабинету Unicom24, по расписанию или кнопкой «Сверить сейчас» — трекер считает строки missing, поставленные в очередь приёма. Обновление отставшего статуса (status_behind) не считается: постбек о такой конверсии приходил. Прогон «только отчёт» (report, режим по умолчанию) ничего не дозаписывает и алерта не даёт: чтобы получать алерт, включите у интеграции дозаливку (backfill). Сверка загруженным файлом в подсчёт не входит.

Событие создаётся, если за прогон дозаписано не меньше 3 конверсий или не меньше 10 % от конверсий, которые сеть отдала за окно сверки. Оба порога меняются в панели: «Алерты» → «Потерянные постбеки», поля «Мин. объём» и «Порог» (0 в «Пороге» отключает проверку доли).

Куда Что приходит
«Внимание» в панели и приложении Пункт на интеграцию: сеть, сколько дозаписано за сутки и в последнем прогоне, пример click id, кнопка «Настройка постбека» (карточка подключения, /postbacks?id=…). Виден администраторам.
Пуш в приложение Тем же администраторам. Нажатие открывает «Внимание» на этом пункте, кнопка «Настройка постбека» ведёт в панель. Отключается в приложении: «Ещё» → «Уведомления» → «Потерянные постбеки» (настройка lost_postbacks, по умолчанию включена). Переключатель есть у администратора, если версия трекера знает этот вид; в старой версии приложения пункта нет, пуши приходят.
Telegram В каналы алертов, если бот подключён и вид алерта включён. Кнопка «Открыть в панели» ведёт на карточку подключения постбеков.

Пуш и сообщение в Telegram уходят не чаще раза в 6 часов на интеграцию («Антиспам-интервал» в настройке алерта). Прогоны внутри этого окна в пункт «Внимания» всё равно попадают, так что счётчик за сутки полный. Пункт можно отметить решённым; он вернётся со следующим уведомлением.

Что проверить, получив алерт: ссылку постбека в кабинете сети (домен, токен подключения, макрос click id) и карантин постбеков — нет ли там тех же конверсий с непривязанным click id.

Исходящие постбеки (отправка конверсий во внешние системы)

По правилам потока конверсия может дополнительно отправляться наружу — очередь в Postgres с ретраями и экспоненциальным бэкоффом, лог каждой попытки доставки. Адаптеры реализованы для: Facebook CAPI, TikTok Events API, Google Ads offline conversion import, GA4 Measurement Protocol, Яндекс.Метрика (офлайн-конверсии), AppMetrica, VK Реклама (офлайн-события) и произвольный custom_http.

Автоимпорт офферов/статистики партнёрских сетей и история ставок

Отдельно от входящих постбеков (которые сеть шлёт вам на каждую конверсию) есть периодический почасовой автоимпорт из API самой сети: актуальный список офферов (название, гео, выплата, статус, капы) и агрегированная статистика конверсий за период — для сверки с вашими постбеками (детект шейва сети). Настраивается в панели, раздел «Интеграции» — учётные данные/токен сети шифруются INOTRACK_INTEG_ENC_KEY (см. обслуживание инстанса); без ключа модуль не падает, просто cost-sync рекламных площадок и этот автоимпорт не запускаются.

⚠️ Как и пресеты входящих постбеков выше, это отправная точка, а не гарантированно рабочая интеграция «из коробки»: API сетей меняются, поэтому вместо отдельного жёстко прошитого клиента под каждую сеть используется один настраиваемый REST/JSON-клиент — реальные HTTP-запросы с таймаутом и ретраями к адресу, который вы сами указываете в конфиге интеграции по документации сети на момент подключения. Сети под этот клиент: Leadgid, Pampadu, Guruleads, 3snet, PIN-UP Partners, Vavada. Подключение сети с другой формой API (нестандартный ответ, не токен+JSON) — доработка продукта, а не настройка; напишите в поддержку.

История ставок: при каждом импорте трекер сравнивает выплату оффера с сохранённой ранее — если сеть изменила payout (в т.ч. тихо срезала), это пишется в историю ставок (таблица payout_history) и сразу уходит алертом в Telegram (тот же канал, что капы/CR/пропажа постбеков) — не по факту в отчёте через месяц. Уникальность оффера сети — по паре (рекламодатель, внешний ID оффера), повторный импорт того же оффера обновляет запись, а не плодит дубли.

Макросы исходящего custom_http

В URL, значениях заголовков и body_template доступны {clickid}, {goal}, {status}, {sum}, {sum_usd}, {sum_rub}, {sum_eur}, {currency}, {txid}, {ext_click_id}, {network}, {conversion_id}, {advertiser_id}, {offer_id}, {source_id}, {sub1}…{sub10}.

{sum} — исходная выплата в валюте сети ({currency}), с сохранением точности. {sum_usd}, {sum_rub} и {sum_eur} — уже рассчитанные трекером суммы в USD, RUB и EUR соответственно, округлённые до двух знаков после точки. Например, если сеть передала 556.8 RUB, а трекер рассчитал 6.187 USD, шаблон https://example.com/pb?sum={sum_usd}&currency=USD отправит https://example.com/pb?sum=6.19&currency=USD. Макрос {currency} по-прежнему возвращает валюту сети: для фиксированной валюты укажите её код в шаблоне явно.

Дальше

Полный список макросов, доступных в ссылках офферов и в постбек-URL — справочник макросов. Публичный API для управления postback-эндпоинтами программно — API.

Словарь значений параметров и сумма по правилу

Входящий маппинг может переводить значения сети в наши названия продуктов и внешние ID офферов, а затем рассчитывать выплату по событию или продукту. Например:

{
  "clickid": "subid",
  "status": "event",
  "status_values": {"approved": "ftd"},
  "sum": "payout",
  "default_currency": "RUB",
  "offer_ext_id": "offer",
  "extra": {"product": "product_code", "order_sum": "order_total"},
  "value_maps": {
    "product": {"card_x": "Кредитная карта"},
    "offer_ext_id": {"network_42": "42"}
  },
  "value_map_defaults": {"product": "Другой продукт"},
  "sum_rules": [
    {"field": "product", "equals": "Кредитная карта", "amount": 500, "currency": "RUB"},
    {"field": "goal", "equals": "ftd", "percent": 30, "percent_of": "order_sum"},
    {"field": "", "amount": 100}
  ],
  "sum_rules_override": false
}

Порядок: обычный разбор → ValueMaps → SumRules → resolveZeroSum. Словари работают только для offer_ext_id и имён из extra: сначала точное совпадение, затем поиск по приведённому к нижнему регистру значению без пробелов по краям. Если совпадения нет, используется непустой value_map_defaults для поля; иначе значение сохраняется. Значение по умолчанию работает и без пар в словаре: в форме можно заполнить только его, оставив список соответствий пустым. Отсутствующее или пустое входное значение при этом остаётся пустым. Изменённые исходные значения видны в extra_raw и offer_ext_id_raw пробного прогона.

Правила проверяются по порядку после словарей. Условие field — status (сырой статус), goal, offer_ext_id или ключ extra; equals сравнивается без учёта регистра и крайних пробелов. Пустой field означает «иначе». Нужно ровно одно действие: положительная amount либо percent (до 1000) от percent_of. База процента берётся сначала из преобразованного extra, затем из сырого параметра сети. Нечитаемая база или нулевой результат пропускают правило; результат процента округляется до шести знаков. Валюта правила нормализуется, включая RUR → RUB; без неё сохраняется валюта маппинга.

По умолчанию правила заполняют только нулевую сумму. sum_rules_override: true разрешает заменить и ненулевую выплату сети. Ненулевая сумма явного правила имеет приоритет над эвристикой карантина unread_sum_param и справочной выплатой оффера. В пробном прогоне доступны sum_source: "sum_rule", sum_rule_index (с нуля), sum_rule_detail; в raw конверсии источник записан как _sum_source: "sum_rule". Без новых настроек поведение прежнее; пустой sum по-прежнему требует allow_no_sum=1. Ограничения: до 30 словарей, по 100 пар (ключ до 64 символов, значение до 200), до 30 правил. Новые настройки хранятся в существующем jsonb без миграции.

Одна цель кабинета на клик

Правило принимает goals (список целей, логическое «или») и once_per_click. Пустой список использует прежнее поле goal; непустой имеет приоритет. Для заявки настройте goals: ["lead", "reject", "approve"], once_per_click: true и сопоставьте все три цели с trk_lead в adapter_config.event_map Метрики. Для VK задайте adapter_config.goal: "trk_lead". Для одобрения оставьте отдельное правило goal: "approve" → trk_approve. Используется нормализованная цель сети.

Параллельные события сериализуются по правилу и клику. queued/retrying/sending/sent резервируют доставку; последующие события пишутся как skipped с причиной already sent for click («пропущено: уже отправлено по клику»). После окончательного failed разрешена новая попытка; ручной повтор старого failed запрещён, если уже есть другая активная или успешная доставка по клику.

В once_per_click правилах Метрики и VK можно задать adapter_config.value (число или строка с числом, включая 0). Иначе используется выплата, при её отсутствии — 0. Метрика берёт yclid, VK — rb_clickid; дочерний клик наследует внешний идентификатор входного клика через parent, если собственного идентификатора нет.

Clickid в составной метке (1xPartners)

Сети семейства 1xPartners (FunPari / fpaff и похожие) могут вернуть всю метку tag=d_<site>m_<ad>c_<clickid> вместо отдельного clickid. В маппинге укажите clickid: "tag" и clickid_extract: "c_([0-9A-HJKMNP-TV-Z]{26})$". В панели это поле «Извлечь clickid (регулярное выражение)»; кнопка «Подставить шаблон 1xPartners» заполняет выражение.

Например, из tag=d_6270318m_191693c_01ARZ3NDEKTSV4RRFFQ69G5FAV будет извлечён 01ARZ3NDEKTSV4RRFFQ69G5FAV. Пробный разбор показывает уже извлечённое значение. Выражение должно соответствовать синтаксису RE2 и содержать ровно одну группу захвата. Пустое выражение отключает извлечение. Если совпадения нет, значение используется целиком: сеть по-прежнему может прислать чистый clickid.

{
  "clickid": "tag",
  "clickid_extract": "c_([0-9A-HJKMNP-TV-Z]{26})$",
  "status": "status",
  "sum": "payout"
}

Гонка pending и окончательного решения

Единица действующего решения — (clickid, txid). Если есть approved или rejected, выбирается последняя по (version, conversion_id) строка среди окончательных решений. Pending (lead, reg, hold) не перекрывает её, даже если принят позже. Без окончательных действует последняя pending. Перерешение approve → reject → approve сохраняет последний approve. Пустой txid означает независимую конверсию.

Окончательное решение сети («одобрено» или «отклонено») приоритетнее статуса «в ожидании»: запоздалый pending не перекрывает его. Этот порядок одинаков для отчётов, прогноза выплат, финансов, правил, бандита и дневных сводок. Окончательные решения учитываются также до начала периода отчёта.

Запоздалый pending по той же сети, клику и транзакции записывается в ClickHouse и историю статусов и отмечается в логе как обратный переход. Исходящие правила для него подавляются уже при наличии окончательного решения; резервирование once_per_click не меняется. Обычные повторные pending по прежнему ключу подавляются входящим дедупом.

Режим цены исходящего правила

POST/PUT/GET /api/internal/postback/rules используют price_mode: payout (по умолчанию), zero, forecast. Неизвестный режим — HTTP 400. PUT без поля возвращает режим payout.

  • payout: исходная выплата; сохраняется фиксированный adapter_config.value для once_per_click Метрики/VK.
  • zero: денежные поля исходящей копии обнуляются. Метрика получает CSV без Price/Currency; VK — e=RG%3A/<goal> без суммы.
  • forecast: выплата × вероятность апрува оффера. Для цели события approve передаётся полная выплата. Фиксированный adapter_config.value не применяется.

Апрув — текущие approved/(approved+rejected) за последние 30 дней, без тестовых и исключённых конверсий, с той же дедупликацией решений, что в прогнозе отчёта. Воркер загружает общий кэш при старте и обновляет раз в 12 минут; отправка не обращается к ClickHouse. Нет истории — справочная вероятность offers.forecast_default_rate (nullable, доля 0..1, задаётся администратором БД), затем 0.5. Текстовые описания апрува сетей автоматически не разбираются. При отказе ClickHouse история заменяется справочником/дефолтом и пишется warning; доставка продолжается. Явный ручной тест правила обновляет прогноз отдельно.

Расчёт меняет только исходящую копию, не финансовую выплату. В response журнала попытки записываются price_mode, price, price_rub, исходные payout, payout_rub и множитель rate; исходный payload сохраняет выплату. Для payout rate равен 1; фиксированная ценность, если задана, видна в price. Креды в эти поля не добавляются.

Формат VK без суммы подтверждён документацией Top.Mail.Ru. Отсутствие суммы может включать ценность цели по умолчанию, заданную в кабинете Top.Mail.Ru.

Тестовый флаг входящего постбека

В param_mapping можно указать test_param — имя параметра сети, например {"test_param":"aptest","test_values":["1","true","yes"]}. Если test_values отсутствует или пуст, используются 1, true, yes. Значения сравниваются точно, с учётом регистра, без удаления пробелов. Пользовательский список заменяет стандартный. Пустой test_param отключает распознавание; отсутствующий параметр означает обычную конверсию. API отклоняет имя с любыми пробельными символами (включая Unicode), пустые значения и список без test_param.

PUT /api/internal/postback/endpoints/{id}/mapping принимает сам объект маппинга в теле запроса, без обёртки param_mapping. Для безопасного разбора POST /api/internal/postback/test передайте этот объект в поле mapping. Эти контракты также описаны в OpenAPI под /api/v1/postback/.

При aptest=1 конверсия сохраняется с is_test=1 в ClickHouse и остаётся в истории. Финансовые запросы и отчёты исключают тестовые строки. Событие конверсии не публикуется в общую шину, поэтому начисления и исходящие постбеки не запускаются. POST /api/internal/postback/test возвращает is_test на верхнем уровне и в parsed; в would_write признак называется IsTest, как остальные поля события. Для тестовой конверсии would_fire_rules пуст. Проверка сама ничего не записывает.

Поддержка