API v1 · 1.4.0

KLO Public APIKLO Public API

REST-интерфейс для управления, анализа и мониторинга вашей клоаки из собственных инструментов - дашбордов, скриптов, ботов - без входа в кабинет. A REST interface to manage, analyze and monitor your cloaking setup from your own tools - dashboards, scripts, bots - without logging into the cabinet.

ОбзорOverview

Чтение: список кампаний и их настройки, счётчики трафика (белая страница / оффер), графики по дням и часам, история кликов, живая лента решений, домены. Запись: создание, изменение, включение/отключение и удаление кампаний, а также готовый файл интеграции. Всё в пределах вашей подписки и тарифа - от лица вашего аккаунта, без входа в кабинет. Read: list campaigns and their settings, traffic counters (white page / offer), day/hour charts, click history, a live decision feed, and domains. Write: create, update, enable/disable and delete campaigns, plus the ready-to-deploy integration file. Everything within your subscription and plan - on behalf of your account, without logging into the cabinet.

Формат ответа. Успех: {"data": …, "meta": …}, где meta может отсутствовать. Ошибка: {"error": {"code": …, "message": …}}. Скачивание интеграции возвращает файл. Пагинация есть у кампаний и истории; домены возвращаются одним списком. Новые совместимые поля не меняют адрес API. Response format. Success: {"data": …, "meta": …}, with optional meta. Error: {"error": {"code": …, "message": …}}. Integration downloads return a file. Campaigns and history are paginated; domains return one list. Compatible new fields do not change the API URL.

Первый запрос за три шагаYour first request in three steps

  1. В кабинете, раздел «API», создайте токен с правом чтения. Для управления кампаниями дополнительно включите запись.Create a read token in Cabinet → API. Enable write access only if your tool needs to manage campaigns.
  2. На своём сервере сохраните токен в переменную окружения KLO_API_TOKEN и выполните пример ниже. Во всех остальных примерах заменяйте klo_live_… на свой токен.On your server, store the token in the KLO_API_TOKEN environment variable and run the example below. Replace klo_live_… in other examples with your own token.
  3. Проверьте HTTP 200, свой тариф и data.limits. Далее запросите /campaigns или /stats. Примеры cURL рассчитаны на Bash; в PowerShell используйте curl.exe и синтаксис своей оболочки.Check for HTTP 200, your plan and data.limits. Then request /campaigns or /stats. cURL examples use Bash syntax; in PowerShell use curl.exe and your shell's quoting syntax.
Bash · cURL
curl --silent --show-error --fail-with-body --max-time 30 \
  -H "Authorization: Bearer $KLO_API_TOKEN" \
  https://cabinet.klo-boostclicks.online/api/v1/account
Это API управления аккаунтом и аналитики. Для проверки посетителя установите файл интеграции из кабинета. Секреты и HMAC-подписи лоадера, а также маршрут /check относятся к другому протоколу; Bearer-токен его не заменяет.This is the account management and analytics API. To check visitors, install an integration from the cabinet. Loader secrets, HMAC signatures and the /check route use a separate protocol; a Bearer token does not replace them.

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

API использует отдельные Bearer-токены вида klo_live_…. Это не тот же секрет, что вшит в лоадер, - токены выпускаются и отзываются отдельно, специально для API. The API uses dedicated Bearer tokens shaped like klo_live_…. These are not the loader secret - API tokens are issued and revoked separately, exclusively for the API.

Как получить токенGetting a token

  • Откройте кабинет → вкладка «API».Open the cabinet → the “API” tab.
  • Нажмите «Создать токен», задайте название.Click “Create token” and give it a name.
  • Скопируйте токен в настройки своего приложения. Новые токены хранятся зашифрованными; в кабинете их можно повторно показать и скопировать. У старого токена может быть доступен только префикс; тогда создайте новый.Copy the token to your application settings. New tokens are stored encrypted and can be revealed and copied again in the cabinet. A legacy token may only expose its prefix; create a replacement in that case.
  • Отзывайте ненужные токены там же. После отзыва запросы API с этим токеном отклоняются. Существующие файлы фильтрации используют отдельный секрет и продолжают работу.Revoke unused tokens there. Revoked tokens can no longer authenticate API requests. Existing filtering files use a separate secret and continue working.

Передавайте токен в заголовке каждого запроса:Send the token in the header of every request:

HTTP
Authorization: Bearer klo_live_1a2b3c4d5e6f7890abcd1234ef567890abcd1234
Храните токен как пароль. Он даёт доступ к данным вашего аккаунта. Не публикуйте его в клиентском (браузерном) коде - API рассчитан на серверные интеграции. При компрометации немедленно отзовите токен в кабинете. Treat the token like a password. It grants access to your account’s data. Do not embed it in client-side (browser) code - the API is designed for server-side integrations. If it leaks, revoke it in the cabinet immediately.

Права токена (scopes)Token scopes

У токена есть уровень доступа. При создании в кабинете выберите «Доступ на запись», если токену нужны операции изменения; иначе токен будет только для чтения. A token carries an access level. When creating one in the cabinet, tick “write access” if the token needs to modify data; otherwise it is read-only.

ScopeЧто разрешеноAllows
readВсе GET-эндпоинты (аккаунт, кампании, статистика, история, лента)All GET endpoints (account, campaigns, stats, history, feed)
read + writeДополнительно - создание, изменение, вкл/выкл и удаление кампаний (v1.1)Additionally - create, update, enable/disable and delete campaigns (v1.1)
Запрос на запись с токеном только для чтения вернёт 403 insufficient_scope.A write request with a read-only token returns 403 insufficient_scope.

Максимум 15 активных токенов на аккаунт. Вместо Authorization поддерживается X-Api-Key, но передавайте только один способ авторизации. Токен в URL не принимается. Для каждого приложения удобно создать отдельный токен с минимальными правами.Up to 15 active tokens per account. X-Api-Key is supported as an alternative to Authorization; use one authentication method per request. URL tokens are not accepted. Use a separate least-privilege token for each application.

Базовый URL и версииBase URL & versioning

Base URL
https://cabinet.klo-boostclicks.online/api/v1

Все пути в этой документации относительны базового URL. Версия зафиксирована в пути (/api/v1). Ломающие изменения выйдут под новой версией; текущая продолжит работать в переходный период. All paths in this documentation are relative to the base URL. The version is pinned in the path (/api/v1). Breaking changes will ship under a new version; the current one keeps working during the transition.

Почему /api/v1, если выпуск 1.4.0? v1 в адресе - основная версия API. 1.4.0 - версия выпуска внутри v1: в нём добавлены новые возможности. Поэтому запрос аккаунта остаётся GET /api/v1/account; адреса /api/v1.4 и /api/v1.4.0 не используются. Текущая версия выпуска указана в info.version OpenAPI-спецификации.Why /api/v1 when the release is 1.4.0? v1 in the URL is the major API version. 1.4.0 is a release within v1 that adds capabilities. Account requests therefore remain GET /api/v1/account; /api/v1.4 and /api/v1.4.0 are not used. The current release is available as info.version in the OpenAPI specification.

Лимиты запросовRate limits

Лимит зависит от тарифа и виден в limits.rate_limit_per_min ответа /account:The limit depends on your plan and is exposed as limits.rate_limit_per_min in /account:

ТарифPlanЗапросов в минутуRequests per minute
START60
PROFIT / TRIAL120
DOMINATION300

Лимит считается отдельно для каждого токена на обслуживающем узле в минутном окне. Ответ после проверки токена содержит заголовки:The limit applies per token on each serving node in a minute window. Responses after token validation include:

HeaderЗначениеMeaning
X-RateLimit-LimitЛимит в текущем окнеLimit for the current window
X-RateLimit-RemainingСколько запросов осталосьRequests remaining
X-RateLimit-ResetUnix-время сброса окнаUnix time when the window resets

При превышении вернётся 429 Too Many Requests с заголовком Retry-After (секунды до сброса). Реализуйте экспоненциальную паузу и повтор.Exceeding it returns 429 Too Many Requests with a Retry-After header (seconds until reset). Implement exponential backoff and retry.

Показатель использования токена в кабинете приблизительный: счётчики находятся в памяти узла, сбрасываются при его перезапуске, сутки считаются по UTC. Он не предназначен для сверки оплаченного трафика.Token usage shown in the cabinet is approximate: counters are node-local, reset after a restart and use UTC days. They are not a billing or traffic reconciliation ledger.

ОшибкиErrors

Ошибки возвращаются с соответствующим кодом HTTP и телом в едином формате:Errors are returned with the appropriate HTTP status and a consistent body:

JSON
{
  "error": {
    "code": "invalid_token",
    "message": "API token is invalid or has been revoked."
  }
}
200 OK 400 bad_request 401 missing_token / invalid_token 403 subscription_inactive / subscription_expired 404 not_found 405 method_not_allowed 429 rate_limit_exceeded 500 internal_error
codeHTTPПричинаReason
missing_token401Нет заголовка AuthorizationNo Authorization header
invalid_token401Токен неверный или отозванToken invalid or revoked
subscription_inactive403Подписка не активнаSubscription not active
subscription_expired403Подписка истеклаSubscription expired
insufficient_scope403Токен без права записи (v1.1)Token lacks write scope (v1.1)
plan_upgrade_required403Тип интеграции недоступен на тарифеIntegration type not allowed on your plan
validation_error422Ошибка в полях запроса (см. details)Invalid request fields (see details)
unsupported_integration422Для этого типа интеграции нет скачиваемого файла (Cloudflare)This integration has no downloadable file (Cloudflare)
idempotency_conflict409Тот же Idempotency-Key использован с другим телом запросаThe same Idempotency-Key was reused with a different request body
not_found404Ресурс не найденResource not found
rate_limit_exceeded429Превышен лимит запросовRate limit exceeded
upstream_error502Не удалось связаться с узлом записиThe write node could not be reached
write_unavailable503Запись временно недоступна; учитывайте Retry-AfterWrites are temporarily unavailable; honor Retry-After
service_unavailable503Хранилище авторизации недоступно; повторите позжеAuthorization storage is unavailable; retry later
write_failed503Не удалось подтвердить сохранение; проверьте результат перед повторомPersistence could not be confirmed; check the result before retrying
payload_too_large413Тело запроса больше 64 КиБThe request body exceeds 64 KiB
unsupported_media_type415Указан неподдерживаемый Content-TypeThe supplied Content-Type is unsupported
При 401 проверьте токен, при 403 подписку и права, при 422 поля из error.details. Повторяйте 429, временные 5xx и сетевые сбои с паузой. Для создания кампании при повторе сохраняйте исходные тело и Idempotency-Key. При сбое прокси тело ответа может оказаться HTML; сначала проверяйте HTTP-статус и Content-Type.For 401 check the token; for 403 check subscription and scopes; for 422 inspect error.details. Retry 429, temporary 5xx and network failures with backoff. For campaign creation retain the original body and Idempotency-Key on retries. A proxy failure may return HTML; inspect the HTTP status and Content-Type before parsing JSON.

ПагинацияPagination

Списки листаются курсором. Параметр limit задаёт размер страницы; в блоке meta приходит next_cursor - передайте его в параметр cursor следующего запроса. Если has_next=false, страниц больше нет. Collections use cursor pagination. The limit parameter sets the page size; the meta block returns next_cursor - pass it as the cursor parameter of the next request. When has_next=false, there are no more pages.

Курсор непрозрачный: не собирайте и не изменяйте его вручную. Для следующей страницы сохраняйте прежние фильтры и limit. При смене фильтра начинайте без курсора. Изменения данных во время обхода могут сдвигать страницы; сверку проводите за завершённый период, устраняя повторы по event_id.Treat cursors as opaque: do not construct or edit them. Keep the same filters and limit for subsequent pages; restart without a cursor after changing filters. Concurrent data changes may shift pages. Reconcile a completed period and deduplicate by event_id.

JSON
{
  "data": [ /* … */ ],
  "meta": {
    "count": 20,
    "total": 57,
    "has_next": true,
    "next_cursor": "eyJvIjoyMH0"
  }
}

Время и полнота данныхTime and data completeness

ts содержит Unix-время в секундах, time, created_at, updated_at и expires_at используют ISO-8601 в UTC. Неизвестная дата может быть null. Даты фильтров и группировка по суткам относятся к часовому поясу статистики кабинета. В графиках используйте meta.timezone, а не часовой пояс браузера.ts is Unix time in seconds; time, created_at, updated_at and expires_at use ISO-8601 UTC. An unknown date may be null. Date filters and daily aggregates use the cabinet statistics timezone. For charts use meta.timezone, not the browser timezone.

История доступна за 7 дней на START и за 45 дней на остальных планах. Точные ограничения возвращает /account. Даты передавайте как YYYY-MM-DD; обе границы включены. Проверяйте фактические meta.from и meta.to: доступный период может быть уже запрошенного.History is available for 7 days on START and 45 days on other plans; /account returns the limits. Use YYYY-MM-DD dates; both boundaries are inclusive. Check effective meta.from and meta.to, as the available period may be narrower than requested.

Аналитика обновляется с задержкой. Проверяйте meta.source и meta.partial, а у счётчиков в объектах кампаний stats_meta. source=clickhouse означает основную аналитику, local_jsonl локальный резерв; partial=true предупреждает о неполной выборке. Не заменяйте последний полный отчёт нулями из неполного ответа: повторите запрос позже.Analytics updates are delayed. Check meta.source and meta.partial, or stats_meta for counters inside campaign objects. source=clickhouse identifies primary analytics; local_jsonl is a local fallback; partial=true warns of incomplete data. Keep the last complete report instead of replacing it with zeros from a partial response, and retry later.

offer и white описывают принятое решение и выданную страницу. Это не подтверждение покупки, просмотра страницы или окончательная оценка человека/бота. unique_ips считает разные IP, а не людей: за одним IP могут быть тысячи посетителей.offer and white describe a decision and the selected page. They do not confirm a purchase, page view or definitive human/bot classification. unique_ips counts IP addresses, not people: one IP may represent thousands of visitors.

АккаунтAccount

GET/account
Профиль аккаунта: тариф, статус подписки, срок действия, домены и лимиты API.Account profile: plan, subscription status, expiry, domains and API limits.
cURL
curl https://cabinet.klo-boostclicks.online/api/v1/account \
  -H "Authorization: Bearer klo_live_…"

ОтветResponse

200 · JSON
{
  "data": {
    "key_id": "key-20260101120000-a1b2c3d4",
    "label": "My account",
    "plan": "profit",
    "plan_restricted": false,
    "active": true,
    "expires_at": "2026-09-13T10:00:00+00:00",
    "created_at": "2026-07-13T14:40:28+00:00",
    "domains": ["example.com"],
    "limits": {
      "rate_limit_per_min": 120,
      "history_window_days": 45
    }
  }
}

Сводная статистикаAccount statistics

GET/stats
Счётчики по всему аккаунту: суммарно и в разбивке по кампаниям. «offer» - показы оффера, «white» - показы белой страницы.Counters across the whole account: totals plus a per-campaign breakdown. “offer” = offer served, “white” = white page served.

today относится к текущим суткам в часовом поясе кабинета. total охватывает сохранённую историю аналитики, а не неограниченный срок жизни аккаунта. Итоги включают трафик без кампании и удалённых кампаний, поэтому могут превышать сумму текущего списка кампаний. Локальный резерв ограничен 45 днями и отмечается partial=true.today uses the cabinet's current calendar day. total covers retained analytics history, not an unlimited account lifetime. Totals include traffic without a campaign and traffic from deleted campaigns, so they may exceed the sum of current campaigns. Local fallback covers up to 45 days and is marked partial=true.

cURL
curl https://cabinet.klo-boostclicks.online/api/v1/stats \
  -H "Authorization: Bearer klo_live_…"

ОтветResponse

200 · JSON
{
  "data": {
    "totals": {
      "today": { "offer": 340, "white": 1290 },
      "total": { "offer": 15803, "white": 64200 }
    },
    "campaigns_count": 3,
    "campaigns": [
      {
        "id": "camp-1a2b3c4d5e6f",
        "name": "Demo LP",
        "is_active": true,
        "today": { "offer": 120, "white": 410 },
        "total": { "offer": 5100, "white": 22010 }
      }
    ]
  }
}

Список кампанийList campaigns

GET/campaigns
Все ваши кампании с краткими настройками и счётчиками. Курсорная пагинация.All your campaigns with a short config summary and counters. Cursor-paginated.

Параметры запросаQuery parameters

ParamTypeОписаниеDescription
limitoptintРазмер страницы, 1–100 (по умолчанию 20)Page size, 1–100 (default 20)
cursoroptstringКурсор следующей страницы из meta.next_cursorNext-page cursor from meta.next_cursor
cURL
curl "https://cabinet.klo-boostclicks.online/api/v1/campaigns?limit=20" \
  -H "Authorization: Bearer klo_live_…"

ОтветResponse

200 · JSON
{
  "data": [
    {
      "id": "camp-1a2b3c4d5e6f",
      "name": "Demo LP",
      "is_active": true,
      "integration_type": "php_direct",
      "white_url": "main.html",
      "offer_url": "d/index.php",
      "redirect_method": "include",
      "created_at": "2026-07-13T14:40:28+00:00",
      "updated_at": "2026-08-01T09:12:00+00:00",
      "stats": {
        "today": { "offer": 120, "white": 410 },
        "total": { "offer": 5100, "white": 22010 }
      }
    }
  ],
  "meta": { "count": 1, "total": 1, "has_next": false }
}

Одна кампанияGet a campaign

GET/campaigns/{id}
Полные (безопасные) настройки кампании, включая таргетинг, и счётчики.Full (safe) campaign settings, including targeting, plus counters.
cURL
curl https://cabinet.klo-boostclicks.online/api/v1/campaigns/camp-1a2b3c4d5e6f \
  -H "Authorization: Bearer klo_live_…"

ОтветResponse

200 · JSON
{
  "data": {
    "id": "camp-1a2b3c4d5e6f",
    "name": "Demo LP",
    "is_active": true,
    "integration_type": "php_direct",
    "white_url": "main.html",
    "offer_url": "d/index.php",
    "targeting": {
      "countries": ["LT"],
      "devices": ["mobile"],
      "operating_systems": [],
      "browsers": [],
      "browser_languages": ["lt"],
      "block_empty_language": true
    },
    "allowed_ips_count": 0,
    "blocked_ips_count": 4,
    "delayed_start_clicks": 5,
    "stats": { "today": { "offer": 120, "white": 410 }, "total": { "offer": 5100, "white": 22010 } }
  }
}
Если кампания не принадлежит вашему токену - 404 not_found.If the campaign does not belong to your token - 404 not_found.

Статистика кампанииCampaign statistics

GET/campaigns/{id}/stats
Счётчики одной кампании (сегодня и за всё время), кластерно-агрегированные.Counters for a single campaign (today and all-time), aggregated cluster-wide.
200 · JSON
{
  "data": {
    "campaign_id": "camp-1a2b3c4d5e6f",
    "today": { "offer": 120, "white": 410 },
    "total": { "offer": 5100, "white": 22010 }
  }
}

История кликовClick history

GET/history
Журнал решений по кликам с фильтрами. Окно по времени ограничено вашим тарифом.A log of per-click decisions with filters. The time window is limited by your plan.

Параметры запросаQuery parameters

ParamTypeОписаниеDescription
fromoptdateС даты, YYYY-MM-DDFrom date, YYYY-MM-DD
tooptdateПо дату, YYYY-MM-DDTo date, YYYY-MM-DD
actionoptstringoffer · white
siteoptstringДоменDomain
campaign_idoptstringИдентификатор своей кампанииYour campaign identifier
reasonoptstringФильтр по причине решенияFilter by decision reason
qoptstringПолнотекстовый поиск (IP, гео, причина…)Full-text search (IP, geo, reason…)
limitoptint10–200 (по умолчанию 50)10–200 (default 50)
cursoroptstringКурсор следующей страницыNext-page cursor
cURL
curl "https://cabinet.klo-boostclicks.online/api/v1/history?action=offer&limit=50" \
  -H "Authorization: Bearer klo_live_…"

ОтветResponse

200 · JSON
{
  "data": [
    {
      "event_id": "0123456789abcdef0123456789abcdef",
      "ts": 1787401200,
      "time": "2026-08-22T12:20:00+00:00",
      "action": "offer",
      "reason": "targeting_match",
      "ip": "5.20.10.4",
      "geo": "LT",
      "site": "example.com",
      "campaign_id": "camp-1a2b3c4d5e6f",
      "browser": "Chrome",
      "device": "mobile",
      "query": "gclid=abc&buyer=demo"
    }
  ],
  "meta": { "count": 50, "total": 1240, "page": 1, "pages": 25, "has_next": true, "next_cursor": "eyJwIjoyfQ" }
}

Живая лентаRealtime feed

GET/realtime
Последние решения для мониторинга в реальном времени. Опрашивайте с интервалом, передавая after = meta.cursor_ts предыдущего ответа.The latest decisions for real-time monitoring. Poll at an interval, passing after = the previous response’s meta.cursor_ts.

Параметры запросаQuery parameters

ParamTypeОписаниеDescription
afteroptintUnix-время в секундах; передайте предыдущий meta.cursor_ts. Сервер повторно читает 180 секунд перед этой отметкой.Unix time in seconds; pass the previous meta.cursor_ts. The server rereads 180 seconds before this timestamp.
limitoptint1–200 (по умолчанию 50)1–200 (default 50)
cURL
curl "https://cabinet.klo-boostclicks.online/api/v1/realtime?after=1787401100" \
  -H "Authorization: Bearer klo_live_…"

ОтветResponse

200 · JSON
{
  "data": [ /* строки как в /history */ ],
  "meta": { "count": 7, "cursor_ts": 1787401242 }
}
Опрос раз в 5-10 секунд обычно достаточен. Повторы ожидаемы: сохраняйте последние непустые event_id и пропускайте уже полученные. В новых событиях это 32 шестнадцатеричных символа; для старых записей в ленте возможен непрозрачный совместимый идентификатор, а в истории пустая строка. Не разбирайте формат ID. Сочетание ts + ip + campaign_id не уникально и может склеить разных посетителей.Polling every 5-10 seconds is usually sufficient. Repeats are expected: remember recent nonempty event_id values and skip those already received. New events use 32 hexadecimal characters; legacy feed events may have an opaque compatibility ID, while history may return an empty string. Do not parse IDs. ts + ip + campaign_id is not unique and may merge different visitors.

meta.limit_hit=true означает, что ответ достиг лимита; это сигнал возможного усечения, а не точное число оставшихся событий.meta.limit_hit=true means the response reached its limit: it warns of possible truncation without counting remaining events.

Это лента мониторинга, а не гарантированная доставка всех событий. При всплеске больше limit, длительном перерыве или большой задержке аналитики часть событий может не попасть в ленту. Не рассчитывайте по ней итоговую статистику и оплату; для сверки используйте историю, агрегаты и экспорт кабинета за завершённый период.This is a monitoring feed, not guaranteed delivery of every event. Bursts above limit, long polling gaps or substantial analytics delays may omit events. Do not calculate final statistics or billing from this feed; reconcile history, aggregates and cabinet exports for a completed period.

v1.2 Клики во времениClicks over time

GET/stats/timeseries
Ряды для графиков: клики по дням или часам. Для одной кампании - /campaigns/{id}/timeseries с теми же параметрами.Chart-ready series: clicks bucketed by day or hour. For a single campaign use /campaigns/{id}/timeseries with the same parameters.

Параметры запросаQuery parameters

ParamTypeОписаниеDescription
granularityoptstringday (по умолчанию)(default) · hour
from, tooptdateПериод YYYY-MM-DD. По умолчанию: 30 дней (day) или текущие сутки (hour)Range YYYY-MM-DD. Defaults: last 30 days (day) or today (hour)
siteoptstringТолько по одному доменуRestrict to a single domain
Окно day ограничено тарифом: 7 дней на START, 45 на остальных. hour возвращает до 7 дней. Слишком широкий период сужается; фактические границы находятся в meta. Некорректная дата или период целиком вне доступного окна возвращает 422.day is limited by plan: 7 days on START, 45 on other plans. hour covers up to 7 days. Wider requests are narrowed; meta contains the effective boundaries. Invalid dates or a period entirely outside the available window return 422.
cURL
curl "https://cabinet.klo-boostclicks.online/api/v1/stats/timeseries?granularity=day&from=2026-08-01&to=2026-08-24" \
  -H "Authorization: Bearer klo_live_…"

ОтветResponse

200 · JSON
{
  "data": [
    { "bucket": "2026-08-22", "offer": 3, "white": 1543, "total": 1546, "unique_ips": 150 },
    { "bucket": "2026-08-23", "offer": 0, "white": 529, "total": 529, "unique_ips": 235 }
  ],
  "meta": {
    "granularity": "day", "from": "2026-08-01", "to": "2026-08-24",
    "points": 2, "timezone": "Europe/Moscow",
    "totals": { "offer": 3, "white": 2072, "total": 2075 },
    "source": "clickhouse", "partial": false
  }
}
Бакеты нарезаны в часовом поясе из meta.timezone. Пустые интервалы не возвращаются - если нужен сплошной ряд, дополните нулями на стороне клиента.Buckets use the timezone from meta.timezone. Empty buckets are omitted - pad the series client-side if you need a continuous line.

v1.2 ДоменыDomains

GET/domains
Домены аккаунта с трафиком за период (по умолчанию 30 дней).Account domains with traffic for the period (30 days by default).

Оба метода доменов принимают необязательные from и to в формате YYYY-MM-DD, с теми же ограничениями, что дневные графики. Список включает привязанные домены, в том числе без трафика; пагинации нет. pass_rate находится в диапазоне 0-1; например, 0.4923 означает 49.23%.Both domain endpoints accept optional from and to dates in YYYY-MM-DD format, with the same limits as daily charts. The unpaginated list includes assigned domains even if they have no traffic. pass_rate ranges from 0 to 1; for example, 0.4923 means 49.23%.

200 · JSON
{
  "data": [
    { "host": "example.com",
      "stats": { "total": 7211, "offer": 3550, "white": 3661, "unique_ips": 1204, "pass_rate": 0.4923 } }
  ],
  "meta": { "count": 8, "from": "2026-07-26", "to": "2026-08-24" }
}
GET/domains/{host}
Детали домена: трафик по дням и топ причин отправки на white - сразу видно, что именно режет трафик.Domain detail: per-day traffic and the top reasons visitors were sent to white - shows exactly what filters the traffic.
200 · JSON
{
  "data": {
    "host": "example.com",
    "stats": { "total": 7211, "offer": 3550, "white": 3661, "unique_ips": 1204, "pass_rate": 0.4923 },
    "days": [ { "date": "2026-08-23", "total": 812, "offer": 401, "white": 411 } ],
    "top_white_reasons": [ { "reason": "hosting", "count": 2085 }, { "reason": "bot_ua", "count": 679 } ]
  }
}
Домен не из вашего аккаунта - 404 not_found. По доменам доступна статистика - ровно то же, что показывает вкладка «Домены» в кабинете.A domain outside your account returns 404 not_found. Domains are analytics-only here - exactly what the cabinet Domains tab shows.

v1.2 Файл интеграцииIntegration file

GET/campaigns/{id}/integration
Отдаёт готовый файл интеграции - тот же, что кнопка «Скачать» в кабинете (ключ и кампания уже вшиты).Returns the ready-to-deploy integration file - the same one the cabinet Download button gives you (key and campaign already embedded).
Для sdk успешный ответ является JSON, а не файлом: data={integration_type:"sdk",package:"@boostclicks/klo",token,campaign_id,endpoint}. Здесь token - публичный токен SDK, не management Bearer и не секрет PHP-лоадера.For sdk, success returns JSON instead of a file: data={integration_type:"sdk",package:"@boostclicks/klo",token,campaign_id,endpoint}. This token is the SDK public token, not a management Bearer token or PHP loader secret.
Успешный ответ содержит файл. Content-Disposition: attachment задаёт имя; Content-Type зависит от интеграции: HTML для JS-фильтра, обычно application/octet-stream для PHP. При ошибке возвращается JSON. Сначала проверяйте HTTP-статус. Файл содержит секрет ключа: храните его как пароль.A successful response contains a file. Content-Disposition: attachment provides the filename; Content-Type depends on the integration: HTML for the JS filter, usually application/octet-stream for PHP. Errors return JSON. Check the HTTP status first. The file embeds your key secret; protect it like a password.
cURL
curl --fail --show-error -OJ https://cabinet.klo-boostclicks.online/api/v1/campaigns/camp-1a2b3c4d5e6f/integration \
  -H "Authorization: Bearer klo_live_…"
Для Cloudflare-кампаний файла нет: используйте API развёртывания. Скачивание возвращает 422 unsupported_integration.Cloudflare campaigns have no integration file: use the deployment API. Downloading returns 422 unsupported_integration.

v1.1 Управление кампаниями (запись)Managing campaigns (write)

Операции записи требуют токен с правом write (см. Права токена). Ограничения по тарифу - те же, что в кабинете. Write operations require a token with the write scope (see Token scopes). Plan limits are the same as in the cabinet.

  • php_direct, php_reverse и sdk доступны на всех тарифах, включая START.php_direct, php_reverse and sdk are available on all plans, including START.
  • javascript, keitaro, wordpress - тариф PROFIT и выше (иначе 403 plan_upgrade_required).javascript, keitaro, wordpress - PROFIT plan or higher (else 403 plan_upgrade_required).
  • cloudflare требует DOMINATION для создания, редактирования, включения, сохранения доступа и развёртывания. Отключение и удаление доступны на любом активном тарифе. API поддерживает создание, изменение, включение, отключение и удаление Cloudflare-кампаний. Чтение статуса своей кампании доступно и на младшем тарифе, чтобы увидеть disabled_plan.cloudflare requires DOMINATION for creation, editing, enabling, saving access and deployment. Disable and delete remain available with any active subscription. The API supports creating, updating, enabling, disabling and deleting Cloudflare campaigns. You can read your campaign status on a lower plan, including disabled_plan.

Допустимые значения таргетингаAllowed targeting values

Значения сверяются точно (регистр нормализуется); неизвестное → 422 validation_error. Пустой список = без ограничения.Values are matched exactly (case is normalized); unknown → 422 validation_error. Empty list = no restriction.

devicesDesktop, Mobile, Tablet
operating_systemsWindows, macOS, Linux, iOS, Android, Chrome OS, Ubuntu, Fedora
browsersChrome, Chromium, Firefox, Safari, Edge, Opera, Samsung Internet, UC Browser, Yandex Browser
countriesISO-3166 alpha-2, напр. LT, USISO-3166 alpha-2, e.g. LT, US
browser_languagesкод языка, напр. lt, enlanguage code, e.g. lt, en
Используйте указанный базовый URL. После успешного ответа изменения могут появиться на другом узле с задержкой. При 5xx сначала выясните результат операции через чтение; создание повторяйте с тем же Idempotency-Key.Use the documented base URL. After a successful write, another node may reflect the change with a delay. On 5xx, check the operation result by reading; retry creation with the same Idempotency-Key.

Передавайте JSON-объект с Content-Type: application/json, не массив. Размер тела до 64 КиБ. Boolean-поля передавайте как true/false, списки как массивы строк. Вложенный объект unique_visit описан ниже. Доступны только перечисленные поля; dns_channel доступен только для чтения, собственные DNS-резолверы через API не задаются.Send a JSON object, not an array, with Content-Type: application/json. Maximum body size: 64 KiB. Send booleans as true/false and lists as string arrays. The nested unique_visit object is described below. Only listed fields are writable; dns_channel is read-only, and the API does not accept custom DNS resolvers.

Создать кампаниюCreate a campaign

POST/campaigns
Создаёт новую кампанию. Обязательны name, white_url, offer_url.Creates a campaign. name, white_url, offer_url are required.

Тело запроса (JSON)Request body (JSON)

FieldTypeОписаниеDescription
namerequiredstringНепустое название, до 120 символовNonempty name, up to 120 characters
white_urlrequiredstringБелая страница (URL или локальный путь)White page (URL or local path)
offer_urlrequiredstringОффер (URL или локальный путь)Offer (URL or local path)
white_target_mode, offer_target_modeoptstringurl (по умолчанию) или local. Для локального файла (напр. main.html) укажите local явно - иначе безсхемное значение считается URL.url (default) or local. For a local file (e.g. main.html) pass local explicitly - otherwise a scheme-less value is treated as a URL.
integration_typeoptstringphp_direct (default) · php_reverse · sdk · javascript · keitaro · wordpress · cloudflare
redirect_methodoptstring302·301·proxy·meta_refresh·js_redirect·iframe·include
white_methodoptstringМетод показа white: значения как у redirect_method. include применяется к локальному файлу; для URL используется 302. Метод оффера по умолчанию 302.White page method: same values as redirect_method. include applies to local files; URLs use 302. The offer method defaults to 302.
countries, devices, operating_systems, browsers, browser_languagesoptstring[]Таргетинг (пустой = без ограничения)Targeting (empty = no restriction)
block_empty_languageoptboolБлокировать пустой языкBlock empty language
allowed_ips, blocked_ipsoptstring[]Списки IPIP lists
delayed_start_clicksoptintПрогрев: первые N кликов → whiteWarm-up: first N clicks → white
utm_modeoptstring"" без проверки, allow пропускать совпадение с шаблоном, deny отклонять совпадение"" disables matching; allow permits a pattern match; deny rejects a match
utm_patternoptstringШаблон параметров запроса, до 500 символов; например ?buyer=demo&gclid={gclid}. Макрос соответствует любому значению.Query parameter pattern, up to 500 characters; for example ?buyer=demo&gclid={gclid}. A macro matches any value.
is_activeoptboolВключена (по умолчанию true)Enabled (default true)
unique_visitoptobjectenabled: bool; scope: domain / campaign / account; ttl_hours: 24 / 168 / 360 / 720; skip_mobile: bool. При PATCH включённой настройки пропущенные вложенные поля сохраняются. Отключение очищает настройку: ответ []. Последующее включение использует значения по умолчанию, если не передать новые: domain, 168, skip_mobile=true.enabled: bool; scope: domain / campaign / account; ttl_hours: 24 / 168 / 360 / 720; skip_mobile: bool. While enabled, PATCH preserves omitted nested fields. Disabling clears the setting and returns []. Re-enabling uses defaults unless supplied: domain, 168, skip_mobile=true.

Для совместимости black_url и black_target_mode принимаются как имена offer_url и offer_target_mode, js как javascript. В новых запросах используйте основные имена. Пустой список таргетинга снимает соответствующее ограничение. Локальный файл относится к серверу клиента, где установлена интеграция.Compatibility aliases: black_url and black_target_mode mean offer_url and offer_target_mode; js means javascript. Prefer canonical names in new requests. An empty targeting list removes that restriction. Local files belong to the client server running the integration.

cURL
curl -X POST https://cabinet.klo-boostclicks.online/api/v1/campaigns \
  -H "Authorization: Bearer klo_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Demo LP",
    "integration_type": "php_direct",
    "white_url": "main.html",
    "white_target_mode": "local",
    "offer_url": "https://offer.example.com/lp",
    "countries": ["LT"],
    "devices": ["Mobile"]
  }'

v1.2 Защита от дублейDuplicate protection

При создании передавайте Idempotency-Key: уникальную строку из 8-128 символов A-Z a-z 0-9 . _ : -. Сохраните ключ и исходное тело для повторов после сбоя. В пределах 24 часов повтор в том же аккаунте вернёт ту же кампанию, HTTP 200 и meta.idempotent_replay=true. Первый успех возвращает 201. Другое тело с прежним ключом возвращает 409 idempotency_conflict. Для новой кампании создавайте новый ключ. Без заголовка защита от дублей отсутствует. Некорректный ключ возвращает 422.For creation send Idempotency-Key: a unique 8-128 character string using A-Z a-z 0-9 . _ : -. Retain it and the original body for retries after a failure. Within 24 hours, a retry in the same account returns the same campaign, HTTP 200 and meta.idempotent_replay=true. Initial success returns 201. A changed body with the same key returns 409 idempotency_conflict. Use a new key for a new campaign. Without the header duplicate protection is unavailable. An invalid key returns 422.

cURL
curl -X POST https://cabinet.klo-boostclicks.online/api/v1/campaigns \
  -H "Authorization: Bearer klo_live_…" \
  -H "Idempotency-Key: my-unique-key-42" \
  -H "Content-Type: application/json" \
  -d '{"name":"Demo LP","white_url":"https://example.com/white","offer_url":"https://example.com/offer","is_active":false}'

ОтветResponse

201 Created · JSON
{ "data": { "id": "camp-9f1c…", "name": "Demo LP", "is_active": true, /* … полный объект кампании … */ } }

Изменить кампаниюUpdate a campaign

PATCH/campaigns/{id}
Частичное обновление: передавайте только изменяемые поля (те же, что в создании).Partial update: send only the fields you change (same fields as create).

Это правило распространяется на вложенный unique_visit: {"unique_visit":{"ttl_hours":168}} меняет только срок, сохраняя enabled, scope и skip_mobile. Для Cloudflare изменение целей ставит асинхронное обновление в очередь и сохраняет cookies миграции. Явно переданный метод 302 сохраняется. Статус развёртывания проверяйте отдельно.This also applies to nested unique_visit: {"unique_visit":{"ttl_hours":168}} changes only the TTL, preserving enabled, scope and skip_mobile. For Cloudflare, target changes queue an asynchronous update and preserve migration cookies. An explicitly supplied 302 method is retained. Check deployment status separately.

PATCH не переводит Cloudflare-кампанию в другой тип интеграции: ответ 409. Для смены типа создайте отдельную кампанию.PATCH cannot change a Cloudflare campaign to another integration type: it returns 409. Create a separate campaign to change the integration type.
cURL
curl -X PATCH https://cabinet.klo-boostclicks.online/api/v1/campaigns/camp-9f1c… \
  -H "Authorization: Bearer klo_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Demo LP v2", "countries": ["LT","LV"] }'
200 · JSON
{ "data": { "id": "camp-9f1c…", "name": "Demo LP v2", /* … */ } }

Включить / отключитьEnable / disable

POST/campaigns/{id}/enable
Включает кампанию. Для отключения - /disable. Тело не нужно.Enables the campaign. Use /disable to turn it off. No body needed.
cURL
curl -X POST https://cabinet.klo-boostclicks.online/api/v1/campaigns/camp-9f1c…/disable \
  -H "Authorization: Bearer klo_live_…"
200 · JSON
{ "data": { "id": "camp-9f1c…", "is_active": false, /* … */ } }
То же можно сделать через PATCH с телом {"is_active": false}.The same can be done via PATCH with body {"is_active": false}.

Удалить кампаниюDelete a campaign

DELETE/campaigns/{id}
Безвозвратно удаляет кампанию.Permanently deletes the campaign.
cURL
curl -X DELETE https://cabinet.klo-boostclicks.online/api/v1/campaigns/camp-9f1c… \
  -H "Authorization: Bearer klo_live_…"
200 · JSON
{ "data": { "id": "camp-9f1c…", "deleted": true } }

v1.4.0 Сохранённый доступ CloudflareSaved Cloudflare access

Эти маршруты управляют сохранённым доступом к существующему аккаунту Cloudflare. Они не создают аккаунт у Cloudflare. Доступ и кампании принадлежат текущему владельцу Bearer-токена; чужой идентификатор не даёт доступа.These routes manage saved access to an existing Cloudflare account. They do not create an account at Cloudflare. Saved access and campaigns belong to the Bearer token owner; another owner's identifier does not grant access.

GET/cloudflare/accounts
Возвращает список сохранённых подключений; нужен scope read.Lists saved connections; requires the read scope.

Каждая запись содержит только id, label, email, account_id и added_at. API не возвращает ключ Cloudflare.Each record contains only id, label, email, account_id and added_at. The API never returns the Cloudflare key.

POST/cloudflare/accounts
Сохраняет доступ. Нужны scope write и тариф DOMINATION.Saves access. Requires the write scope and DOMINATION.
FieldTypeОписаниеDescription
emailrequiredstringEmail аккаунта CloudflareCloudflare account email
api_keyrequiredstringCloudflare Global API Key. Передаётся только в теле запроса по HTTPS; API Token вместо Global API Key не подходит.Cloudflare Global API Key. Send it only in the HTTPS request body; an API Token cannot replace the Global API Key.
account_idrequiredstringИдентификатор аккаунта Cloudflare: 32 hex-символаCloudflare account ID: 32 hexadecimal characters
labeloptstringУдобное название подключенияA label for the connection

Ответ содержит те же безопасные метаданные, что GET. Используйте возвращённый id как credential_id при развёртывании.The response contains the same safe metadata as GET. Use its returned id as credential_id when deploying.

Зоны CloudflareCloudflare zones

GET/cloudflare/accounts/{id}/zones
Получает зоны сохранённого подключения, принадлежащие указанному аккаунту Cloudflare. Нужен scope read. {id} - ID сохранённого доступа, а не Cloudflare Account ID.Returns zones owned by the Cloudflare account behind the saved connection. Requires read. {id} is the saved access ID, not the Cloudflare Account ID.
QueryОписаниеDescription
domainНеобязательный фильтр домена, например example.comOptional domain filter, for example example.com
page, per_pageНомер и размер страницы: page=1..10000, per_page=5..50 (по умолчанию 20). Это постраничная пагинация; не используйте cursor. Фактические параметры и итог страницы возвращаются в meta.Page number and size: page=1..10000, per_page=5..50 (default 20). This route uses page pagination, not cursor. Effective pagination parameters and totals are returned in meta.

Каждая зона в data содержит id, domain, status и name_servers. При передаче zone_id в развёртывание API повторно проверяет принадлежность зоны сохранённому аккаунту и соответствие домена.Each zone in data contains id, domain, status and name_servers. Deployment rechecks that the supplied zone_id belongs to the saved account and matches the domain.

Статус Cloudflare-кампанииCloudflare campaign status

GET/campaigns/{id}/cloudflare
Возвращает статус, конфигурацию и сведения об очереди развёртывания. Нужен scope read и своя кампания. Чтение доступно после перехода на младший тариф, в том числе для статуса disabled_plan.Returns deployment status, configuration and queue information. Requires read and an owned campaign. Status remains readable after a plan downgrade, including disabled_plan.

Поля data: campaign_id, status, is_active, domain, credential_id, zone_id, worker_name, route_id, deployed_at, queued, queue, error_code. queue равен null или содержит created_at, next_check_at, check_count. Завершение подтверждает status="deployed". При повторе с Idempotency-Key возвращается исходный ответ приёма; актуальное состояние всегда читайте через GET.The data fields are campaign_id, status, is_active, domain, credential_id, zone_id, worker_name, route_id, deployed_at, queued, queue, and error_code. queue is null or contains created_at, next_check_at, and check_count. Only status="deployed" confirms completion. Idempotency-Key replay returns the original acceptance; always GET the current state.

Все Cloudflare GET-запросы направляются на основной узел, чтобы состояние очереди и сохранённого доступа не зависело от задержки репликации. Ответы не содержат API-ключей или исходных диагностик Cloudflare.All Cloudflare GET routes use the primary node, so queue and saved-access state do not depend on replica lag. Responses contain no API keys or raw Cloudflare diagnostics.

Успешная запись кампании и завершённое развёртывание - разные события. После 202 опрашивайте этот маршрут с паузой; не отправляйте deploy на каждый опрос.A saved campaign and a completed deployment are separate events. After 202, poll this route with backoff; do not send deploy on every poll.
domain - точное имя зоны Cloudflare (обычно корневой домен). Произвольный поддомен, URL с путём и wildcard не подходят. Отключение/удаление сначала сохраняет локальное состояние. Если удаление ресурсов на Cloudflare не подтверждено, API возвращает 503 cf_cleanup_failed; повторите тот же disable/delete. Для DELETE сохранена запись восстановления, поэтому повтор возможен после локального удаления кампании.domain must be the exact Cloudflare zone name, normally an apex domain. Arbitrary subdomains, URLs with paths, and wildcards are not accepted. Disable/delete first persists local state. If Cloudflare resource removal is unconfirmed, the API returns 503 cf_cleanup_failed; retry the same disable/delete. DELETE keeps a recovery record so it can be retried after local campaign removal.

Развёртывание, повтор и проверка NSDeploy, retry and check nameservers

Все три POST-маршрута требуют scope write и DOMINATION. Существующий фоновый обработчик выполняет развёртывание асинхронно. Запрос API не ждёт установки Worker.All three POST routes require write and DOMINATION. The existing background processor deploys asynchronously. The API request does not wait for Worker installation.

POST/campaigns/{id}/cloudflare/deploy
Привязывает доступ и домен, затем ставит развёртывание в очередь.Binds saved access and a domain, then queues deployment.
FieldTypeОписаниеDescription
credential_idrequiredstringID своего сохранённого доступа из /cloudflare/accountsYour saved access ID from /cloudflare/accounts
domainrequiredstringДомен развёртывания, например example.comDeployment domain, for example example.com
zone_idoptstringID зоны из списка зон; API проверяет владельца и домен. Если не передан, зона определяется по домену.A zone ID from the zone list; the API verifies ownership and domain. If omitted, the zone is resolved from the domain.

HTTP 202 означает, что задача принята в очередь. Для уже развёрнутой кампании с той же конфигурацией ответ 200 не запускает повторную установку. Повторный запрос для уже ожидающей задачи сохраняет существующую запись очереди и её состояние.HTTP 202 means the job was accepted into the queue. For an already deployed campaign with identical configuration, 200 does not reinstall it. Repeating a request for a pending job preserves the existing queue entry and state.

Поддерживается Idempotency-Key на 24 часа. Ключ ограничен текущим аккаунтом, операцией и кампанией. Повторяйте запрос с тем же ключом и телом; другое тело в этой области возвращает 409 idempotency_conflict. Новый запрос для другой кампании или другой операции имеет отдельную область ключей.Idempotency-Key is supported for 24 hours, scoped to the current owner, operation and campaign. Retry with the same key and body; a different body within that scope returns 409 idempotency_conflict. A different campaign or operation has a separate key scope.

Смена уже активного или развёрнутого домена возвращает 409. Для другого домена создайте отдельную кампанию.Rebinding an already active or deployed domain returns 409. Create a separate campaign for another domain.
POST/campaigns/{id}/cloudflare/retry
Повторяет развёртывание с сохранённой конфигурацией. Передайте JSON-объект {}. Ответ 202 означает очередь; существующая ожидающая задача сохраняется.Retries deployment using saved configuration. Send the JSON object {}. A 202 response means queued; an existing pending job is preserved.
POST/campaigns/{id}/cloudflare/check-ns
Проверяет делегирование NS для сохранённого домена. Передайте JSON-объект {}. Если делегирование ещё ожидается, ответ 200 сообщает результат проверки. Если можно продолжить развёртывание, ответ 202 ставит задачу в очередь.Checks nameserver delegation for the saved domain. Send the JSON object {}. While delegation is pending, 200 returns the check result. When deployment can proceed, 202 queues the job.

API использует тот же жизненный цикл кампании, что кабинет: изменение целей вызывает обновление, включение - восстановление, отключение и удаление - очистку интеграции. Установка обновления API сама по себе не меняет Worker у клиентов.The API uses the same campaign lifecycle as the cabinet: target changes trigger an update, enabling restores deployment, and disabling or deleting cleans up the integration. Installing the API update alone does not change customer Workers.

Cloudflare: полный пример cURLCloudflare: complete cURL example

Пример для Bash. Все значения ниже - заглушки. Подставьте свой токен с read и write, существующий аккаунт Cloudflare и собственный домен. После каждого шага проверьте HTTP-статус; замените ID на возвращённые API. Требуется DOMINATION.Bash example. All values below are placeholders. Supply your own token with read and write, an existing Cloudflare account and your domain. Check the HTTP status after each step and replace IDs with returned values. Requires DOMINATION.

Bash · cURL
export KLO_API_TOKEN='KLO_BEARER_TOKEN_PLACEHOLDER'
BASE='https://cabinet.klo-boostclicks.online/api/v1'

# 1. Save access to your existing Cloudflare account.
curl --silent --show-error --fail-with-body --max-time 30 -X POST "$BASE/cloudflare/accounts" \
  -H "Authorization: Bearer $KLO_API_TOKEN" -H 'Content-Type: application/json' \
  -d '{"label":"Example account","email":"owner@example.com","api_key":"GLOBAL_API_KEY_PLACEHOLDER","account_id":"00000000000000000000000000000000"}'

# 2. Use data.id returned above; list zones and note the selected zone ID.
CREDENTIAL_ID='SAVED_ACCESS_ID_PLACEHOLDER'
curl --silent --show-error --fail-with-body --max-time 30 --get "$BASE/cloudflare/accounts/$CREDENTIAL_ID/zones" \
  -H "Authorization: Bearer $KLO_API_TOKEN" \
  --data-urlencode 'domain=example.com' --data-urlencode 'page=1' --data-urlencode 'per_page=20'

# 3. Create a Cloudflare campaign. No Worker is deployed by this request.
curl --silent --show-error --fail-with-body --max-time 30 -X POST "$BASE/campaigns" \
  -H "Authorization: Bearer $KLO_API_TOKEN" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: example-cf-campaign-001' \
  -d '{"name":"Example Cloudflare","integration_type":"cloudflare","white_url":"https://white.example.com/","offer_url":"https://offer.example.com/","white_target_mode":"url","offer_target_mode":"url","white_method":"302","redirect_method":"302","unique_visit":{"enabled":true,"scope":"campaign","ttl_hours":24,"skip_mobile":false}}'

# 4. Use data.id returned by campaign creation and the zone ID from step 2.
CAMPAIGN_ID='CAMPAIGN_ID_PLACEHOLDER'
curl --silent --show-error --fail-with-body --max-time 30 -X POST "$BASE/campaigns/$CAMPAIGN_ID/cloudflare/deploy" \
  -H "Authorization: Bearer $KLO_API_TOKEN" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: example-cf-deploy-001' \
  -d "{\"credential_id\":\"$CREDENTIAL_ID\",\"domain\":\"example.com\",\"zone_id\":\"ZONE_ID_PLACEHOLDER\"}"

# 5. Poll status with backoff; 202 means queued, not deployed.
curl --silent --show-error --fail-with-body --max-time 30 "$BASE/campaigns/$CAMPAIGN_ID/cloudflare" \
  -H "Authorization: Bearer $KLO_API_TOKEN"

# 6. After setting the required nameservers at your registrar, check delegation.
curl --silent --show-error --fail-with-body --max-time 30 -X POST "$BASE/campaigns/$CAMPAIGN_ID/cloudflare/check-ns" \
  -H "Authorization: Bearer $KLO_API_TOKEN" -H 'Content-Type: application/json' -d '{}'

# 7. Update one nested setting; other unique_visit fields stay unchanged.
curl --silent --show-error --fail-with-body --max-time 30 -X PATCH "$BASE/campaigns/$CAMPAIGN_ID" \
  -H "Authorization: Bearer $KLO_API_TOKEN" -H 'Content-Type: application/json' \
  -d '{"unique_visit":{"ttl_hours":168}}'

# 8. If deployment needs a retry, use the saved configuration.
curl --silent --show-error --fail-with-body --max-time 30 -X POST "$BASE/campaigns/$CAMPAIGN_ID/cloudflare/retry" \
  -H "Authorization: Bearer $KLO_API_TOKEN" -H 'Content-Type: application/json' -d '{}'

Если домен ещё не делегирован, используйте name_servers из ответа списка зон и повторите проверку после обновления DNS. Для повторного deploy после сетевого сбоя сохраните исходное тело и ключ; для состояния используйте GET.If the domain is not delegated yet, use name_servers from the zone list and check again after DNS updates. Retain the original body and key when retrying deploy after a network failure; use GET to inspect state.

v1.2 OpenAPI-спецификацияOpenAPI specification

Машиночитаемый контракт (OpenAPI 3.1) - по нему генерируются клиенты и импортируется коллекция в Postman/Insomnia:A machine-readable contract (OpenAPI 3.1) - generate clients from it or import it into Postman/Insomnia:

URL
https://cabinet.klo-boostclicks.online/api/openapi.json

Спецификация доступна без токена, описывает маршруты, параметры и схемы ответа. Перед генерацией клиента скачайте актуальную версию. Клиент должен допускать новые поля и учитывать ограничения, описанные выше.The specification is public and describes routes, parameters and response schemas. Download the current version before generating a client. Clients should tolerate new response fields and honor the limits documented above.

Пример: клиент на летуExample: generate a client
# OpenAPI Generator
npx @openapitools/openapi-generator-cli generate \
  -i https://cabinet.klo-boostclicks.online/api/openapi.json \
  -g python -o ./klo-client

ОбъектыObjects

КампанияCampaign

ПолеFieldTypeОписаниеDescription
idstringИдентификатор кампанииCampaign identifier
namestringНазваниеName
is_activeboolВключена ли кампанияWhether the campaign is enabled
integration_typestringphp_direct, php_reverse, sdk, javascript, keitaro, wordpress, cloudflare
white_urlstringБелая страницаWhite page target
offer_urlstringОффер (целевая страница)Offer (target page)
white_target_mode, offer_target_modestringВ подробном ответе: url или localIn detailed responses: url or local
white_method, redirect_methodstringМетод показа white и оффераWhite and offer delivery methods
targetingobjectГео, устройства, ОС, браузеры, языкиGeo, devices, OS, browsers, languages
allowed_ips, blocked_ipsstring[]В подробном ответе: списки IP. Поля с суффиксом _count содержат их размеры.In detailed responses: IP lists. Fields ending in _count contain their sizes.
utm_mode, utm_patternstringВ подробном ответе: режим и шаблон метокIn detailed responses: query matching mode and pattern
delayed_start_clicksintВ подробном ответе: количество кликов прогреваIn detailed responses: warm-up click count
unique_visitobject | []В подробном ответе: enabled, scope, ttl_hours, skip_mobile; значения и частичный PATCH описаны в создании.In detailed responses: enabled, scope, ttl_hours, skip_mobile; see creation for values and partial PATCH behavior.
dns_channelobjectТекущий DNS-канал: enabled, а при включении zone и server. Только чтение; собственные резолверы не поддерживаются.Current DNS channel: enabled, and zone/server when enabled. Read-only; custom resolvers are unsupported.
created_at, updated_atstring | nullДаты в ISO-8601 UTC; могут отсутствовать у старых записейISO-8601 UTC dates; legacy records may have unknown dates
statsobjecttoday / total → {offer, white}
stats_metaobjectИсточник и полнота счётчиков: source, partialCounter source and completeness: source, partial

Событие историиHistory event

ПолеFieldTypeОписаниеDescription
event_idstringНепрозрачный ID для устранения повторов. Новый ID содержит 32 hex-символа; старые записи истории могут иметь пустую строку.Opaque ID for deduplication. New IDs have 32 hexadecimal characters; legacy history records may have an empty string.
tsintUnix-время событияUnix time of the event
timestringТо же в ISO-8601 (UTC)Same in ISO-8601 (UTC)
actionstringoffer · white
reasonstringПричина решенияDecision reason
ip, geostringIP и страна посетителяVisitor IP and country
browser, devicestringБраузер и тип устройстваBrowser and device type
site, campaign_id, querystringДомен, ID кампании и строка параметров запроса; неизвестное значение пустоеDomain, campaign ID and query string; unknown values are empty

История измененийChangelog

ВерсияVersionИзмененияChanges
v1.4.0Cloudflare: сохранённый доступ, зоны аккаунта, асинхронное развёртывание, статус, повтор и проверка NS; жизненный цикл кампании через API. SDK на START, Fedora, unique_visit с частичным вложенным PATCH, поля dns_channel для чтения.Cloudflare saved access, owned zones, asynchronous deployment, status, retry and nameserver checks; campaign lifecycle through the API. SDK on START, Fedora, unique_visit with partial nested PATCH, and read-only dns_channel.
v1.2Тайм-серии (день/час), домены, фильтр campaign_id в истории, скачивание файла интеграции, Idempotency-Key, лимиты по тарифу, OpenAPI-спека.Time-series (day/hour), domains, campaign_id filter in history, integration file download, Idempotency-Key, plan-based rate limits, OpenAPI spec.
v1.1Запись: создание, изменение, вкл/выкл и удаление кампаний (scope write; ограничения по тарифу).Write access: create, update, enable/disable and delete campaigns (write scope; plan limits).
v1.0Первый выпуск: аккаунт, кампании, статистика, история, живая лента (только чтение).Initial release: account, campaigns, statistics, history, realtime feed (read-only).
Для одной кампании доступен GET /campaigns/{id}/timeseries. /domains возвращает аналитику доменов; привязка домена Cloudflare выполняется через развёртывание.Per-campaign charts are available through GET /campaigns/{id}/timeseries. /domains returns domain analytics; bind a Cloudflare domain through deployment.