Перейти к содержанию

Разработка (API)

У трекера три внешних интерфейса:

  • приём кликов - обычный заход посетителя по ссылке;
  • Click API - серверный вызов, который отдаёт решение движка в JSON;
  • приём конверсий (постбек) - отправка конверсии из партнёрки;
  • плюс Admin API - тот же REST, что использует панель, доступный по ключу.

Приём кликов

Клик принимается GET-запросом на домен трекера:

https://<домен>/<alias>?sub_id_1=...&external_id=...&cost=...

Первый сегмент пути - alias или token кампании. «Голый» домен ведёт на кампанию по умолчанию (domains.is_default). Ответ - редирект, лендинг или действие, в зависимости от того, какой поток выбран.

Методы трекинга (параметр frm):

Значение Что делает
pixel Записывает клик и отдаёт прозрачный GIF 1×1 без редиректа (email-трекинг)
script Отдаёт document.location.replace(...) с типом application/javascript - для вставки тегом <script src=...>

Других значений frm движок не обрабатывает: любое иное значение равносильно его отсутствию.

Клик по CTA лендинга (клик-аут):

https://<домен>/click?_ci=<сабка>

_ci - публичная сабка клика; если параметра нет, берётся одноимённая кука. Обработчик находит сохранённый Offer URL, записывает LP-клик (по нему считаются метрики «LP клики» и «CTR лендинга») и отправляет посетителя на оффер. Если сабка неизвестна или адрес оффера не сохранён - 404.

Click API

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

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

Адрес

GET|POST https://<домен>/click_api/v3?token=<токен кампании>

Версии в пути: v1, v2, v3, v4. Принимаются все четыре, ответ всегда одной формы (той, что понимают живые клиенты). Другой метод, кроме GET и POST, даёт 405. Тело POST ограничено 1 МиБ.

Хост запроса не проверяется - в отличие от обычного клика. Кампанию называет токен, а зовут API с чужого сервера, у которого своё имя.

Аутентификация

Только token кампании - тот, что лежит в её карточке. Alias для этого не годится: он публичен и стоит в ссылке креатива. Неверный токен даёт 401 без уточнения причины, чтобы перебором нельзя было выяснить, какие кампании существуют.

Если лицензия трекера недействительна, Click API отвечает 503 - иначе он стал бы обходом остановки трафика.

Задайте кампании alias. Ссылка приёма кликов собирается из alias, а если его нет - из того же токена. То есть у кампании без alias «секретный» ключ Click API уезжает в креатив вместе со ссылкой. Токен сверяется с учётом регистра.

Параметры

Служебные (в клик не попадают):

Параметр Что задаёт
token Токен кампании. Обязателен
log=1 Добавить в ответ текстовый лог обработки
info=1 Добавить в ответ блок info с решением движка
ip Адрес посетителя
user_agent User-Agent посетителя
language Значение Accept-Language
referrer / se_referrer Реферер
x_requested_with Заголовок X-Requested-With (пакет приложения)
landing_id На каком лендинге показан клик - записывает LP-клик
force_redirect_offer=1 Вести сразу на оффер, минуя лендинг
uniqueness_cookie, uri, method, version Принимаются и игнорируются (совместимость)

Все остальные параметры уезжают в клик как параметры ссылки - sub_id_1..30, utm_*, cost, external_id, keyword и прочее.

Поддерживается и вложенная форма, которой пользуется PHP-клиент: get_params[имя] (настоящая query посетителя), headers[имя], server[REMOTE_ADDR]. Значения заголовков очищаются от управляющих символов.

Адрес посетителя берётся в порядке: параметр ip -> server[REMOTE_ADDR] -> адрес самого вызывающего. Последний вариант врёт про страну, зато виден в логе.

landing_id сверяется со справочником лендингов: неизвестный идентификатор LP-клик не запишет (иначе в отчёт можно было бы налить нажатия на чужой лендинг).

Ответ

{
  "status": "302 Found",
  "headers": ["HTTP/1.1 302 Found", "Location: https://example.com/ok", "Content-Type: text/html; charset=utf-8"],
  "body": "<a href=\"https://example.com/ok\">Found</a>.\n\n",
  "contentType": "text/html; charset=utf-8",
  "cookies_ttl": 24,
  "uniqueness_cookie": "",
  "log": ["кампания 15 (Кампания)", "визитор: IP 8.8.8.8, ...", "выбран поток 7 (Поток)"],
  "info": {
    "campaign_id": 15, "stream_id": 7, "landing_id": 0, "offer_id": 0,
    "sub_id": "tjdxzn.2.bwdaui", "token": "tjdxzn.2.bwdaui",
    "type": "location", "url": "https://example.com/ok",
    "is_bot": false,
    "uniqueness": {"campaign": false, "stream": true, "global": false}
  }
}
Поле Смысл
status Строка статуса того ответа, который получил бы браузер
headers Заголовки строками «Имя: значение». Для не-2xx первой идёт статусная строка
body Тело ответа (для редиректа - заглушка). Обрезается по 1 МиБ, обрезка отмечается в логе
contentType Content-Type ответа
cookies_ttl Срок жизни кук кампании в часах (0 в настройках = 24)
uniqueness_cookie Всегда пусто: уникальность по кукам мы не считаем
log Текстовый лог обработки, только при log=1
info Решение движка, только при info=1

Поля info:

  • campaign_id, stream_id, landing_id, offer_id - что выбрал движок (0 = не выбрано);
  • sub_id и token - публичная сабка клика (одно и то же значение). Её нужно подставить в Offer URL и в параметр _ci при клик-ауте;
  • type - location (есть заголовок Location), http_code (ответ не 2xx) или content (отдана страница);
  • url - адрес перехода, если type=location;
  • is_bot - вердикт бот-детекта;
  • uniqueness - уникален ли клик в области кампании, потока и трекера целиком.

Пример

curl "https://<домен>/click_api/v3?token=<токен>&info=1&log=1\
&ip=8.8.8.8&user_agent=Mozilla/5.0%20(Windows%20NT%2010.0)%20Chrome/120\
&sub_id_1=fb&landing_id=12"

Приём конверсий (Postback)

https://<домен>/postback?subid={click_id}&status=sale&payout=10
https://<домен>/<секретный код>/postback?subid=...

Второй формат - совместимость с Keitaro: партнёрки клиентов, переехавших оттуда, настроены именно так. Секретный код задаётся в настройках; если он задан, обращение без него на путь с кодом даёт 403.

Значение subid резолвится в клик: сначала по публичной сабке, затем по external_id. Индекс живёт 90 дней.

Имена всех параметров настраиваемы (Настройки -> постбек). Умолчания:

Смысл Принимаемые имена
Идентификатор клика subid, sub_id, click_id, trackID, subaccount, s, external_id
Статус status, type, goal
Выплата payout, revenue, profit, sum
Идентификатор транзакции transaction_id, txid, trans_id, tid
Валюта currency, cur, payout_currency

Берётся значение первого непустого параметра из списка.

Ответы:

Ситуация Код Тело
Принято 200 ok
Точный повтор 200 ok (duplicate)
Тот же статус 200 ok (same status)
Переход статуса запрещён 200 ok (transition not allowed)
Redis недоступен, запрос отложен на диск 200 ok (deferred)
Нет subid 400 no subid
Неверный секретный код 403 incorrect postback code
Клик не найден 404 click not found
Отложить не удалось 503 temporary failure

Подробнее про статусы, ребиллы и выплаты - в разделе Конверсии и постбеки и в глоссарии.

Admin API

Тот же интерфейс, что использует панель (/api/v1/*), доступен по API-ключу без логина. Ключ передаётся заголовком:

Api-Key: tm_<ключ>
# или
Authorization: Bearer tm_<ключ>

Ключ создаётся в «Настройки -> API-ключи» (только админ) и показывается один раз. Ключ наследует права своего пользователя: ключ баера видит только его разделы и группы и получает 403 на админских адресах. Подробности модели прав - в разделе Управление командой.

Панель ходит по тому же API с сессионной кукой, поэтому за Caddy базовый адрес выглядит как https://<домен>/admin/api/v1/..., а напрямую к сервису - http://<IP>:8081/api/v1/....

Ответы - JSON. Тело запроса ограничено 1 МБ (файлы лендингов идут отдельными обработчиками со своими пределами). Мутации (POST/PUT/DELETE) пишутся в журнал действий.

Примеры

# список кампаний
curl -H "Api-Key: tm_xxx" https://<домен>/admin/api/v1/campaigns

# карточка кампании
curl -H "Api-Key: tm_xxx" https://<домен>/admin/api/v1/campaigns/42

# отчёт
curl -H "Api-Key: tm_xxx" \
  "https://<домен>/admin/api/v1/reports/clicks?group_by=country&group_by2=os&date_from=2026-07-01&date_to=2026-07-31"

Вход и справочники

Адрес Что делает
POST /api/v1/auth/login Вход по логину и паролю, ставит сессионную куку. 429 при переборе
POST /api/v1/auth/logout Выход
GET /api/v1/me Кто я: id, логин, роль, группы, доступные разделы
GET /api/v1/meta Справочники для интерфейса: типы фильтров, схемы и действия потоков, типы редиректов, типы стоимости, валюты, часовые пояса, методы уникальности, типы конверсий, IP сервера, брендинг
GET /api/v1/brand Брендинг (публичный, нужен экрану входа)
GET /api/v1/setup, POST /api/v1/setup/activate Активация лицензии на чистой установке (публичные)
GET /api/v1/license Состояние лицензии
GET /api/v1/domains/verify?domain= Служебный ответ для Caddy: выпускать ли сертификат на этот хост

Кампании и потоки

Адрес Права
GET/POST /api/v1/campaigns раздел campaigns
GET/PUT/DELETE /api/v1/campaigns/{id} раздел campaigns + группа кампании
POST /api/v1/campaigns/{id}/clone то же
PUT /api/v1/campaigns/{id}/domains то же
PUT /api/v1/campaigns/{id}/labels то же
GET /api/v1/campaigns/{id}/conversions то же - конверсии одной кампании
POST /api/v1/campaigns/{id}/update_costs то же - ручное обновление расхода (ниже)
POST /api/v1/campaigns/{id}/streams раздел streams + группа кампании
PUT /api/v1/campaigns/{id}/streams/order то же
PUT/DELETE /api/v1/streams/{id} раздел streams + группа кампании потока
PUT /api/v1/streams/{id}/state то же - включить/выключить, не трогая остальные поля
POST /api/v1/streams/{id}/clone то же

Потоки приходят внутри GET /api/v1/campaigns/{id}, отдельного списка потоков нет.

Простые сущности

Единый набор CRUD: GET /api/v1/<сущность>, POST /api/v1/<сущность>, PUT /api/v1/<сущность>/{id}, DELETE /api/v1/<сущность>/{id}.

Сущность Раздел ACL
offers, landings, domains, traffic_sources, affiliate_networks, geo_profiles, groups одноимённый раздел
postback_rules глобальные правила - только админ, правила кампании - её группа
users, teams, labels, custom_metrics, ip_whitelist, triggers, integrations только админ

Отдельно:

  • POST /api/v1/domains/{id}/check - ручная проверка доступности и SSL одного домена;
  • GET/PUT /api/v1/users/{id}/groups, GET/PUT /api/v1/users/{id}/resources - управление правами (только админ);
  • GET/POST/DELETE /api/v1/favourites - избранное пользователя, скоупится по владельцу.

Facebook Conversions API

Отдельный набор адресов, все только для админа:

Адрес Что делает
GET /api/v1/integrations/facebook Список интеграций
GET /api/v1/integrations/facebook/options Справочник для формы: статусы, события Meta, умолчания
POST /api/v1/integrations/facebook Создать (201)
PUT /api/v1/integrations/facebook/{id} Изменить
DELETE /api/v1/integrations/facebook/{id} Удалить
POST /api/v1/integrations/facebook/{id}/check Проверить связь с Meta

Тело создания и изменения:

{"name":"Facebook - основной кабинет","enabled":true,
 "pixel_id":"1234567890123456","access_token":"EAAG...",
 "test_event_code":"","api_version":"v21.0",
 "event_map":{"sale":"Purchase","lead":"Lead"},
 "campaign_ids":[12,34]}

Пустой access_token при PUT означает «оставить прежний». В ответах токена нет никогда - вместо него has_token и огрызок token_hint. Универсальный GET /api/v1/integrations тоже вырезает колонку с токеном.

Проверка связи возвращает {"ok":true,"fail":"","message":"...","pixel_name":"..."}; поле fail принимает значения auth, data, transient. Код ответа 200 даже когда ok:false - неудачная проверка это не ошибка запроса.

Импорт конверсий

Адрес Что делает
GET /api/v1/conversions/import Пределы: срок хранения, максимальный размер и число строк, длина поля, порог фонового режима
GET /api/v1/conversions/import?job=<uuid> Состояние фоновой задачи
POST /api/v1/conversions/import[?force=1] Загрузка файла: multipart/form-data, поле file

Только админ и раздел conversions. Файл больше 2 МБ разбирается в фоне - ответ 202 с job, дальше опрашивайте состояние. force=1 отменяет проверку отпечатка файла.

max_bytes, max_rows, max_field, max_age_days панель берёт у движка - он этот предел и применяет, так что цифра из ответа всегда та, на которой файл действительно отобьётся. Если движок не ответил, в ответе появляется "limits_from_engine": false, а max_bytes показывает собственный потолок панели - справка не должна ронять экран, но и выдавать чужую цифру за точную она не будет.

Получения одной записи по id (GET /api/v1/offers/{id} и т.п.) у простых сущностей нет - только список.

Архив

DELETE у кампаний, потоков, лендингов, офферов, источников трафика, партнёрских сетей и пользователей не удаляет, а архивирует: строка остаётся с state='deleted' и отметкой времени. Движок читает только активные сущности, поэтому из раздачи трафика архивная запись выпадает сразу.

Адрес Что делает
GET /api/v1/<раздел>?state=deleted список архива вместо живых записей
POST /api/v1/<раздел>/{id}/restore вернуть из архива (в состояние active)
GET /api/v1/archive срок хранения, сколько чего в архиве и сколько пережило срок
GET /api/v1/archive/{table} что лежит в архиве раздела: id, name, deleted_at
POST /api/v1/archive/purge окончательно удалить пережившее срок; {"days":N}, {"table":"offers"}

GET /api/v1/archive/{table} существует отдельно от списков не для симметрии: у потоков собственного списка нет вовсе (они приходят внутри кампании), и без этой ручки архивный поток был бы недостижим.

Домены не архивируются - имя домена уникально, и архивная запись не дала бы завести его заново. POST .../restore на неархивируемом разделе возвращает 400, на живой записи - 404 (а не молчаливое «ок»: иначе кнопка в устаревшей вкладке делала бы вид, что сработала).

Чистка (purge) сносит вместе со строками файлы лендингов и офферов. При архивации файлы не трогаются: иначе восстановленный лендинг был бы пустой папкой. Срок берётся из настройки archive_ttl (по умолчанию 90 дней, потолок 3650).

Файлы лендингов и офферов

Для landings и offers одинаковый набор (раздел одноимённый, плюс группа записи):

Адрес Что делает
POST /api/v1/{landings\|offers}/upload Залить ZIP новой записью
GET /api/v1/{landings\|offers}/{id}/files Дерево файлов
GET /api/v1/{landings\|offers}/{id}/file?path= Прочитать файл
PUT /api/v1/{landings\|offers}/{id}/file Сохранить файл
DELETE /api/v1/{landings\|offers}/{id}/file?path= Удалить файл
POST /api/v1/{landings\|offers}/{id}/file/rename Переименовать или перенести
POST /api/v1/{landings\|offers}/{id}/file/upload Залить один файл
GET /api/v1/{landings\|offers}/{id}/download Выгрузить папку архивом
POST /api/v1/{landings\|offers}/{id}/replace Заменить содержимое папки (id и папка сохраняются)
GET /api/v1/{landings\|offers}/{id}/preview Предпросмотр по подписанному короткому токену

Отчёты

Адрес Раздел ACL
GET /api/v1/dashboard dashboard
GET /api/v1/reports/clicks reports
GET /api/v1/reports/conversions conversions
GET /api/v1/reports/clicks-log clicks
GET /api/v1/reports/trends trends
GET /api/v1/reports/postbacks conversions

Общие параметры: date_from, date_to (YYYY-MM-DD; по умолчанию последние 7 дней). Период считается в часовом поясе трекера, он же возвращается полем timezone.

GET /api/v1/reports/clicks дополнительно принимает:

  • group_by - измерение группировки (по умолчанию campaign_id);
  • group_by2 - второй уровень группировки;
  • f_<измерение>=значение - фильтр. Параметр можно повторить: f_country=UA&f_country=PL означает «любая из двух», а не «последняя выиграла»;
  • f_<измерение>_op=<условие> - условие сравнения (по умолчанию eq, а при нескольких значениях in): eq, ne, in, not_in, contains, not_contains, starts, ends, gt, gte, lt, lte, between (ровно две границы, включительно), regexp, not_regexp, empty, not_empty. Двум последним значение не нужно - достаточно самого f_<измерение>_op=empty;
  • filters=<json> - дерево И/ИЛИ, когда плоских параметров мало: {"op":"and","items":[{"dim":"country","operator":"in","values":["UA","PL"]}, {"op":"or","items":[{"dim":"os","operator":"eq","value":"Android"}]}]}. Вложенность до 5 уровней, до 100 условий в группе, до 200 значений в списке. Заданное вместе с f_... объединяется по И, а не заменяет его;
  • date_basis=conversion - считать по дате конверсии, а не по дате клика. В этом базисе клик-метрики (уники, боты, расход, LP-клики) не применимы и равны нулю, а clicks означает число конвертнувших кликов.

Измерения: campaign_id, stream_id, ts_id, offer_id, landing_id, domain, country, region, city, language, os, os_version, browser, browser_version, device_type, device_model, connection_type, isp, operator, referrer, source, search_engine, keyword, external_id, creative_id, ad_campaign_id, x_requested_with, sub_id_1..sub_id_10, а также календарные day, hour, week, month, year, day_of_week, hour_of_day. Выдача ограничена 1000 строками.

Фильтровать можно шире, чем группировать. Дополнительно к измерениям выше принимаются user_agent, ip, sub_id, visitor_code, destination, status, parent_campaign_id, affiliate_network_id, флаги is_bot, is_using_proxy, is_empty_referrer, is_unique_global, is_unique_campaign, is_unique_stream, landing_clicked, is_lead, is_sale, is_reg, is_rejected, is_trash, денежные cost, revenue, lead_revenue, sale_revenue, deposit_revenue, reg_revenue, rejected_revenue, trash_revenue, счётчики deposits, rebills, а также sub_id_11..sub_id_30 и extra_param_1..extra_param_10.

Тип колонки учитывается: у числовых сравнение числовое (f_cost=9&f_cost_op=gt найдёт и 10, и 100 - строковое сравнение поставило бы «10» перед «9»), у флагов принимаются 1/0, true/false, yes/no, у ip сравнение идёт с текстовым представлением адреса. Нечисло в числовой колонке - ошибка 400, а не молчаливый ноль.

Неизвестное имя измерения возвращает 400. Раньше оно молча игнорировалось, и ответ приходил без фильтра - неотличимый от отфильтрованного.

В строке отчёта приходят базовые величины (clicks, uniques, bots, cost, conversions, revenue, leads, sales, rejected, deposits, lp_clicks, roi); производные метрики считаются из них - формулы в глоссарии.

GET /api/v1/reports/clicks-log принимает campaign_id, q (подстрочный поиск по click_id, сабке, external_id, IP, sub_id_1..5, ключевому слову, источнику, идентификатору кампании и креатива сети, рефереру и адресу назначения) и limit (по умолчанию 300, максимум 1000). Поиск идёт на сервере по всему периоду, а не по уже отданной странице.

GET /api/v1/reports/trends принимает granularity=hour для почасового графика.

Лог отправленных постбеков

GET /api/v1/reports/postbacks?date_from=&date_to=&limit=300

Раздел conversions плюс изоляция по группам: в логе лежат адреса постбеков вместе с сабками и ключами партнёрок в строке запроса.

По строке на каждую исходящую отправку. Поля: datetime, kind (s2s или webhook), conversion_id, click_id, sub_id, tid, campaign_id, ts_id, offer_id, type_id, status, url (уже с подставленными макросами), method, http_code (0 - ответа не было вовсе), ok, attempts, duration_ms, response (начало тела ответа), error. Имена кампании, источника, оффера и лендинга подставляются в ответ дополнительными полями.

Фильтры (точное совпадение): числовые campaign_id, source_id, offer_id, type_id, http_code, ok; строковые kind, status, sub_id, tid, method; по идентификаторам conversion_id, click_id. limit по умолчанию 300, максимум 1000.

Это тот отчёт, по которому видно, что постбек отправлен, но не доставлен: успехом считается только код ответа ниже 400.

Ручное обновление расхода

POST /api/v1/campaigns/{id}/update_costs

Проставляет расход задним числом - так работают Dolphin, Fbtool и любой другой сервис учёта трат: рекламная сеть отдаёт настоящую стоимость связки только на следующие сутки.

Права: раздел campaigns плюс группа кампании. Чужую кампанию не обновить даже зная её id.

{
  "start_date": "2026-08-06 00:00:00",
  "end_date": "2026-08-06 23:59:59",
  "cost": "60.83",
  "currency": "USD",
  "timezone": "Europe/Berlin",
  "filters": {"sub_id_5": "120212558973560058"}
}
  • cost принимается и строкой, и числом; запятая как десятичный разделитель допускается. Отрицательное значение и не-число отвергаются;
  • сумма относится ко всему срезу, а не к одному клику: она делится на число кликов среза. Так же считает Keitaro, и иначе цифры в трекере и в кабинете сервиса разошлись бы;
  • даты принимаются как YYYY-MM-DD, YYYY-MM-DD HH:MM или YYYY-MM-DD HH:MM:SS. Дата без времени в end_date означает включительно весь день;
  • timezone - имя IANA; не указан - берётся пояс трекера;
  • currency - код ISO 4217; не указана - берётся валюта трекера;
  • filters режут срез по любому измерению отчёта, кроме календарных и campaign_id (он задан адресом);
  • период шире 366 дней отвергается: обновление расхода переписывает куски партиций ClickHouse, и «обнови за всю историю» кладёт запись кликов.

Ответ:

{"success": true, "updated": true, "clicks": 2, "cost": 12.5,
 "cost_per_click": 6.25, "currency": "USD",
 "from": "2026-08-06 21:00:00", "to": "2026-08-07 20:59:59"}

Если в срезе нет кликов, ответ всё равно успешный, но с "updated": false и пояснением: сервис учёта шлёт траты по каждой связке подряд, и повторять всю выгрузку из-за пустого среза не нужно. Ошибки приходят в форме {"success": false, "error": "..."}.

Глобальный поиск

GET /api/v1/search?q=<строка>&type=<тип>&sort=name|recent

Ищет сразу по кампаниям, потокам, офферам, лендингам, доменам, источникам трафика и партнёрским сетям. Отдельного раздела ACL у маршрута нет: права проверяются внутри - по разделу каждой сущности и по её группе, ровно по тем же правилам, что и в списках соответствующего раздела.

Запрос короче двух символов ничего не ищет и отвечает подсказкой. Символы % и _ экранируются, то есть ищутся буквально. По каждому типу отдаётся не больше 20 строк.

{"query": "docs", "results": [
  {"type": "campaign", "id": 15, "name": "Кампания", "detail": "alias", "state": "active"},
  {"type": "stream", "id": 7, "name": "Поток", "detail": "Кампания", "state": "active"}
]}

Что ищется в каждом типе: кампании - по имени, алиасу и токену; потоки - по имени (в detail приходит имя кампании); офферы - по имени и URL; лендинги - по имени, папке и URL; домены - по имени хоста; источники и партнёрские сети - по имени.

Курсы валют

GET /api/v1/currency_rates
PUT /api/v1/currency_rates

Только админ. Курсы приводят суммы в разных валютах к валюте трекера при построении отчётов.

{"currency": "USD", "rates": {"EUR": 1.09, "UAH": 0.024}}

PUT заменяет набор целиком: частичное обновление опаснее, чем полезнее - забытый курс молча продолжал бы считать по старому. Отбрасываются некорректные коды (нужны ровно три заглавные латинские буквы), неположительные и нечисловые значения, а также сама валюта трекера - её курс к себе всегда 1.

Приведение делается на чтении: смена курса меняет и прошлые отчёты. Строки, записанные без кода валюты (до появления колонки), считаются уже в валюте трекера.

Справочник шаблонов источников

GET /api/v1/source_templates

Раздел traffic_sources. Отдаёт готовые шаблоны рекламных сетей для карточки источника: код, название, признак «популярный» и набор параметров с их плейсхолдерами ({CampaignId}, {AdGroupId} и т.п.) и человеческими названиями. На момент проверки в справочнике 176 сетей.

Отдельным адресом, а не внутри /meta, потому что справочник весит около 140 КБ, а /meta панель запрашивает при каждом входе.

Настройки и обслуживание (только админ)

Адрес Что делает
GET/PUT /api/v1/settings Глобальные настройки трекера
GET /api/v1/audit Журнал действий
GET /api/v1/system Диагностика: состояние хранилищ, очереди, воркера
GET /api/v1/system/update Состояние обновления
POST /api/v1/system/update/check, POST /api/v1/system/update/run Проверить и поставить обновление (работу делает хост-агент)
GET/POST/DELETE /api/v1/api_keys Ключи Admin API
GET /api/v1/maintenance/storage Объём статистики
GET/POST /api/v1/maintenance/cleanup Предпросмотр и запуск очистки статистики
POST /api/v1/maintenance/ttl Применить срок хранения

Доступны любому авторизованному пользователю:

Адрес Что делает
GET/PUT /api/v1/prefs Настройки интерфейса самого пользователя
POST /api/v1/config/rebuild Форсировать пересборку конфигурации движка

Чего в API нет

Полезно знать заранее, если планируется автоматизация:

  • нет OpenAPI/Swagger-спецификации - перечень адресов только здесь;
  • нет получения одной простой сущности по id (GET /api/v1/offers/{id} и т.п.);
  • нет операций архива: удаление окончательное, восстановления нет;
  • нет пакетных запросов;
  • из готовых интеграций свои адреса есть только у Facebook Conversions API (см. ниже); Google Ads, TikTok и Cloudflare - через универсальную сущность integrations (вебхуки).

Совместимости с Admin API Keitaro нет ни на одном адресе: отличаются база пути, имена сущностей и форма отчётов. Скрипт, написанный под Keitaro, придётся переписать.

Расширение (для разработчика продукта)

Новые типы фильтров, действий и редиректов добавляются в сервис traffic (пакеты filters и router), измерения отчётов - в admin (groupDims). Каталогов расширений (свой фильтр или макрос файлом, без пересборки) у трекера нет.