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

Постбэки

slimTDS обрабатывает постбэк-трафик в двух направлениях: входящий (партнёрские сети уведомляют slimTDS о конверсиях) и исходящий (slimTDS пересылает сигналы о конверсиях на ваши собственные трекинг-эндпоинты). Оба направления отвязаны от обработки клика, поэтому ни одно из них не блокирует редиректы посетителей.

Лог конверсий в админке slimTDS

Партнёрские сети вызывают /postback, чтобы сообщить о конверсиях. Принимаются и GET (query-строка), и POST (form-encoded или JSON-тело). Параметры query-строки имеют приоритет над ключами тела, когда присутствуют оба.

ПараметрПримечания
tokenТокен постбэка — обязателен. Либо токен конкретного оффера, либо catch-all токен уровня кампании.
subidID клика (значение макроса {click_id}, помещённое в URL оффера). Обязателен для токенов оффера; необязателен для токенов кампании.
payoutЧисловая сумма выплаты (по умолчанию 0).
statusОдно из approved, pending, hold, rejected (по умолчанию approved).
external_idСобственный идентификатор конверсии партнёра (хранится для сверки).

slimTDS определяет токен в два шага:

  1. Токен оффера — у каждого оффера есть уникальный postback_token (случайная строка из 32 hex-символов). При использовании этого токена конверсия атрибутируется этому конкретному офферу. subid должен быть валидным ID клика, уже записанным в stats.clicks.
  2. Catch-all токен уровня кампании — у каждой кампании тоже есть postback_token (строка из 32 hex-символов, производная от UUID). Это позволяет одному URL постбэка обрабатывать любой оффер внутри кампании; фактический оффер определяется из строки клика по subid. Если subid не передан, пинг записывается как анонимная конверсия кампании (см. ниже).

Если токен не совпадает ни с оффером, ни с кампанией, эндпоинт возвращает 404.

Конверсии вставляются (upsert) в core.conversions с уникальным индексом по click_id. Повторная отправка того же subid обновляет payout, status, external_id и updated_at, а не создаёт дубликат строки. NULL в PostgreSQL считаются различными, поэтому несколько анонимных пингов (без subid) могут сосуществовать без конфликта.

Когда используется токен уровня кампании без subid — например, тестовая кнопка партнёра, не пробрасывающая ID кликов — slimTDS записывает строку конверсии с click_id = NULL и offer_id = NULL, привязанную только к кампании. Это позволяет операторам подсчитывать события партнёра, даже когда атрибуция к конкретному клику недоступна.

Когда приходит конверсия и у совпавшего оффера настроен один или несколько URL постбэков (JSONB-массив postback_urls в core.offers), slimTDS ставит в очередь по одной строке core.postback_deliveries на каждый URL. Строка создаётся синхронно во время входящего постбэк-запроса, но сам HTTP-вызов никогда не делается из потока запроса.

Cron-команда postback:deliver (расписание: * * * * *, каждую минуту) берёт до 50 ожидающих строк и отправляет каждую как HTTP GET через cURL. При успехе (HTTP 2xx или 3xx) строка помечается delivered_at = now(). При неудаче (4xx, 5xx или сетевая ошибка) счётчик попыток увеличивается, а следующая попытка планируется с экспоненциально растущей задержкой.

Задержки индексируются по номеру попытки (с нуля). Делается до 5 попыток всего.

ПопыткаЗадержка перед повтором
1-й повтор1 минута
2-й повтор5 минут
3-й повтор25 минут
4-й повтор2 часа
5-й повтор10 часов

После 5 неудач строка больше не берётся в работу. Суммарный бюджет повторов — примерно 13,5 часов от первой неудачи.

Шаблоны URL постбэков раскрываются перед доставкой. Поддерживаются следующие токены:

ТокенПодставляетсяДоступен в исходящих постбэках
{click_id}ID клика (UUIDv7), изначально переданный как {click_id} в URL оффераДа
{payout}Сумма выплаты из входящего постбэкаДа
{status}Статус конверсии (approved, pending, hold, rejected)Да
{external_id}Значение external_id партнёраДа
{currency}Код валюты из оффера (например, USD)Да
{visitor_uuid}Постоянный UUID посетителяНет — остаётся буквально
{country}Код страны ISO 3166-1 alpha-2Нет — остаётся буквально
{region}Регион / субъектНет — остаётся буквально
{city}Название городаНет — остаётся буквально
{device}Тип устройства (mobile, tablet, desktop)Нет — остаётся буквально
{os}Операционная системаНет — остаётся буквально
{browser}Название браузераНет — остаётся буквально
{lang}Значение Accept-LanguageНет — остаётся буквально
{ip}IP-адрес посетителяНет — заглушка 0.0.0.0
{ua}Строка User-AgentНет — заглушка -
{referer}HTTP RefererНет — остаётся буквально
{lander_host}Хост лендинга-источникаНет — остаётся буквально
{lander_domain}Домен лендинга без TLDНет — остаётся буквально
{lander_button}Сегмент кнопки/пути лендингаНет — остаётся буквально
{campaign_slug}Slug кампанииНет — остаётся буквально
{timestamp}Unix-таймстампНет — время доставки, не клика
{utm_source}UTM sourceНет — остаётся буквально
{utm_medium}UTM mediumНет — остаётся буквально
{utm_campaign}UTM campaignНет — остаётся буквально
{utm_term}UTM termНет — остаётся буквально
{utm_content}UTM contentНет — остаётся буквально
{rand:MIN-MAX}Случайное целое в диапазоне от MIN до MAXНет — остаётся буквально
{randstr:N}Случайная Base58-строка длины NНет — остаётся буквально

Нераспознанные токены остаются в URL без изменений.