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

Входящие постбэки
Заголовок раздела «Входящие постбэки»Партнёрские сети вызывают /postback, чтобы сообщить о конверсиях. Принимаются и GET (query-строка), и POST (form-encoded или JSON-тело). Параметры query-строки имеют приоритет над ключами тела, когда присутствуют оба.
Параметры
Заголовок раздела «Параметры»| Параметр | Примечания |
|---|---|
token | Токен постбэка — обязателен. Либо токен конкретного оффера, либо catch-all токен уровня кампании. |
subid | ID клика (значение макроса {click_id}, помещённое в URL оффера). Обязателен для токенов оффера; необязателен для токенов кампании. |
payout | Числовая сумма выплаты (по умолчанию 0). |
status | Одно из approved, pending, hold, rejected (по умолчанию approved). |
external_id | Собственный идентификатор конверсии партнёра (хранится для сверки). |
Модель токенов
Заголовок раздела «Модель токенов»slimTDS определяет токен в два шага:
- Токен оффера — у каждого оффера есть уникальный
postback_token(случайная строка из 32 hex-символов). При использовании этого токена конверсия атрибутируется этому конкретному офферу.subidдолжен быть валидным ID клика, уже записанным вstats.clicks. - 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, привязанную только к кампании. Это позволяет операторам подсчитывать события партнёра, даже когда атрибуция к конкретному клику недоступна.
Исходящие S2S-постбэки
Заголовок раздела «Исходящие S2S-постбэки»Когда приходит конверсия и у совпавшего оффера настроен один или несколько 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 постбэка
Заголовок раздела «Макросы URL постбэка»Шаблоны 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 без изменений.