CitadelTracker · Routing · Callbacks

Integration guide

Покрокова інтеграція Citadel

Від створення buyer-а до перевіреного callback. Ідіть зверху вниз: кожен крок має результат, який варто перевірити перед наступним.

Як це працює

Один click_id з’єднує весь шлях

  1. Реклама веде не прямо на landing, а на tracking URL Citadel.
  2. Tracker створює клік і робить redirect на активну воронку з click_id.
  3. Landing зберігає click_id і додає його до заявки.
  4. Intake перевіряє токен кампанії, consent, дублікати й доступний cap.
  5. Citadel асинхронно надсилає лід buyer-у; buyer повертає статус callback-ом.
Крок 1

Створіть Buyer, Campaign і Funnel

Buyer

Вкажіть endpoint URL, API key, метод API/Webhook, payout, daily/total cap і статус Active.

Campaign

Виберіть GEO, vertical, buyer, payout і Active. Скопіюйте збережений Intake Token.

Funnel

Прив’яжіть landing URL до кампанії, задайте мову й статус Active.

Перевірка: buyer, campaign і funnel активні; campaign прив’язана до buyer-а, funnel — до campaign.

Крок 2

Скопіюйте готове посилання в Admin → Funnels

Нічого складати вручну не потрібно.

Після збереження Funnel Citadel автоматично створює її tracking URL з правильними ID кампанії та воронки.

  1. Відкрийте Admin → Traffic → Funnels.
  2. Знайдіть потрібну воронку.
  3. Натисніть «Копіювати» у колонці «Трекер». Повний URL уже міститиме домен, campaign і funnel.
  4. Вставте скопійоване посилання в рекламу замість прямого URL landing page.
  5. Кнопка «Тест» відкриває посилання й дозволяє одразу перевірити redirect.

Також готове посилання є всередині кожної Funnel у полі Tracking URL:

https://YOUR_DOMAIN/track/click/?campaign=12&funnel=7

source, sub1…sub5 і cost — необов’язкові параметри для розширеної attribution. Для першого запуску вони не потрібні.

Citadel відповість redirect-ом:

https://landing.example/form?click_id=<generated-id>

Перевірка: відкриття tracking URL переводить на правильний landing, а в URL є click_id.

Крок 3 · що вставити в landing

Citadel не читає сайт сам — форма повинна надіслати йому дані

Простими словами:

Людина переходить на landing через Citadel. У URL з’являється click_id. Код нижче запам’ятовує цей номер, а після натискання кнопки «Відправити» передає номер разом з ім’ям і телефоном.

3.1 Знайдіть форму та додайте/замініть її на цей HTML

Вставте блок у те місце сторінки, де повинна бути форма. Тексти й CSS-класи можна змінювати, але значення id та name залиште як у прикладі.

<form id="lead-form">
  <input type="hidden" name="click_id" id="click_id">

  <label>
    Ім’я
    <input type="text" name="first_name" required>
  </label>

  <label>
    Телефон
    <input type="tel" name="phone" required>
  </label>

  <label>
    Email
    <input type="email" name="email">
  </label>

  <label>
    <input type="checkbox" name="consent_terms" required>
    Погоджуюся з умовами
  </label>

  <label>
    <input type="checkbox" name="consent_data_processing" required>
    Погоджуюся на обробку персональних даних
  </label>

  <label>
    <input type="checkbox" name="consent_marketing">
    Погоджуюся отримувати маркетингові повідомлення
  </label>

  <button type="submit">Відправити</button>
  <p id="form-message" aria-live="polite"></p>
</form>

3.2 Одразу після форми вставте цей JavaScript

Вам потрібно змінити лише country і language, якщо landing не для Польщі.

<script>
(function () {
  const form = document.getElementById('lead-form');
  const message = document.getElementById('form-message');
  const submitButton = form.querySelector('button[type="submit"]');

  // Беремо click_id з URL і запам’ятовуємо його до відправлення форми.
  const urlClickId = new URLSearchParams(window.location.search).get('click_id');
  if (urlClickId) localStorage.setItem('citadel_click_id', urlClickId);
  document.getElementById('click_id').value =
    urlClickId || localStorage.getItem('citadel_click_id') || '';

  form.addEventListener('submit', async function (event) {
    event.preventDefault();
    submitButton.disabled = true;
    message.textContent = 'Відправляємо…';

    const data = new FormData(form);
    const payload = {
      click_id: data.get('click_id') || '',
      first_name: data.get('first_name'),
      phone: data.get('phone'),
      email: data.get('email') || '',
      country: 'PL',
      language: 'pl',
      consent_terms: data.has('consent_terms'),
      consent_data_processing: data.has('consent_data_processing'),
      consent_marketing: data.has('consent_marketing'),
      consent_source: 'landing-form'
    };

    try {
      // Цей файл створимо на сервері в наступному пункті.
      const response = await fetch('/submit-lead.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
      });
      const result = await response.json();

      if (!response.ok) throw new Error(result.error || 'Помилка відправлення');

      message.textContent = 'Дякуємо! Заявку прийнято.';
      form.reset();
      localStorage.removeItem('citadel_click_id');
    } catch (error) {
      message.textContent = 'Не вдалося відправити. Спробуйте ще раз.';
      submitButton.disabled = false;
      console.error(error);
    }
  });
})();
</script>

На цьому браузерна частина готова: форма збирає дані, пам’ятає click_id і відправляє все у файл /submit-lead.php на вашому сервері.

Крок 4 · серверний файл

Створіть submit-lead.php поруч із сайтом

Чому потрібен окремий файл?

Citadel вимагає секретний Intake Token. Якщо вставити його у JavaScript, будь-який відвідувач побачить токен. PHP-файл зберігає токен на сервері й непомітно пересилає заявку в Citadel.

У кореневій папці landing — зазвичай там, де лежить index.html або index.php — створіть файл submit-lead.php і вставте:

<?php
header('Content-Type: application/json; charset=utf-8');

// ЗМІНІТЬ ТІЛЬКИ ЦІ ДВА ЗНАЧЕННЯ:
$citadelUrl = 'https://YOUR_CITADEL_DOMAIN/api/leads/intake/';
$intakeToken = 'YOUR_CAMPAIGN_INTAKE_TOKEN';

$input = json_decode(file_get_contents('php://input'), true);

if (!is_array($input)) {
    http_response_code(400);
    echo json_encode(['error' => 'Немає даних форми']);
    exit;
}

$payload = [
    'click_id' => $input['click_id'] ?? '',
    'first_name' => $input['first_name'] ?? '',
    'phone' => $input['phone'] ?? '',
    'email' => $input['email'] ?? '',
    'country' => $input['country'] ?? '',
    'language' => $input['language'] ?? '',
    'consent_data_processing' => !empty($input['consent_data_processing']),
    'consent_terms' => !empty($input['consent_terms']),
    'consent_marketing' => !empty($input['consent_marketing']),
    'consent_source' => $input['consent_source'] ?? 'landing-form'
];

$ch = curl_init($citadelUrl);
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 15,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'X-Intake-Token: ' . $intakeToken
    ],
    CURLOPT_POSTFIELDS => json_encode($payload)
]);

$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);

if ($body === false) {
    http_response_code(502);
    echo json_encode(['error' => 'Citadel недоступний', 'detail' => $curlError]);
    exit;
}

http_response_code($status ?: 502);
echo $body;

Що саме замінити

  1. YOUR_CITADEL_DOMAIN → домен, де відкривається ваш Citadel.
  2. YOUR_CAMPAIGN_INTAKE_TOKEN → відкрийте потрібну Campaign в Admin і скопіюйте її Intake Token.
  3. У JavaScript вище змініть PL і pl на GEO та мову цього landing, якщо потрібно.

Готовий шлях: реклама → Citadel tracker → landing із click_id → форма → submit-lead.php → Citadel Lead Intake.

4.1 Як перевірити без здогадок

  1. Відкрийте tracking URL кампанії у звичайному браузері.
  2. Після redirect перевірте, що адреса landing містить ?click_id=....
  3. Заповніть форму та натисніть «Відправити».
  4. Має з’явитися повідомлення «Дякуємо! Заявку прийнято».
  5. Відкрийте Admin → Leads: новий лід повинен бути там із Click ID.
Якщо landing зроблений на Tilda, WordPress, Webflow або іншому конструкторі:

HTML/JavaScript вставляється через блок custom code, але PHP-файл може не підтримуватися платформою. Тоді потрібен webhook/serverless function. Сам принцип і поля не змінюються.

Технічний приклад прямого запиту до Citadel

Це робить PHP-файл автоматично. Приклад наведено лише для діагностики:

curl -X POST 'https://YOUR_DOMAIN/api/leads/intake/' \
  -H 'Content-Type: application/json' \
  -H 'X-Intake-Token: YOUR_CAMPAIGN_TOKEN' \
  -d '{
    "click_id": "CLICK_ID_FROM_URL",
    "first_name": "Anna",
    "phone": "+48123123123",
    "consent_data_processing": true,
    "consent_terms": true,
    "consent_marketing": false
  }'
201 {"id": 123, "status": "new"}Новий лід прийнято.
200 {"id": 124, "status": "duplicate"}Заявка прийнята як дублікат і не відправлена buyer-у.

Важливо: consent_data_processing і consent_terms мають бути true; маркетингова згода передається завжди, але може бути false.

Крок 5

Прийміть лід на стороні buyer-а

Citadel викликає налаштований Buyer endpoint методом POST. Якщо API key збережено коректно, він надходить як Authorization: Bearer ….

{
  "lead_id": 123,
  "first_name": "Anna",
  "last_name": "Kowalska",
  "phone": "+48123123123",
  "email": "anna@example.com",
  "country": "PL",
  "language": "pl",
  "ip": "203.0.113.10",
  "source": "facebook",
  "sub1": "ad_42",
  "sub2": "", "sub3": "", "sub4": "", "sub5": ""
}
  • lead_id використовуйте як idempotency key: повторна доставка не повинна створювати дубль.
  • 2xx означає успішну доставку; 4xx — остаточне відхилення.
  • Network error або 5xx запускає до трьох повторних спроб з інтервалом 60 секунд.
Крок 6

Поверніть статус ліда callback-ом

Buyer використовує власний API key і той lead_id, який отримав у payload.

curl -X POST 'https://YOUR_DOMAIN/api/leads/123/status/' \
  -H 'Content-Type: application/json' \
  -H 'X-Buyer-Key: YOUR_BUYER_KEY' \
  -d '{
    "status": "ftd",
    "payout": "45.00",
    "external_id": "buyer-8891",
    "comment": "Deposit confirmed"
  }'

Дозволені статуси: valid, invalid, rejected, ftd. Поле payout застосовується лише для ftd.

Перевірка: відповідь містить актуальні id, status, ftd_at, payout та buyer_lead_id.

Крок 7

Пройдіть тест перед запуском трафіку

  1. Відкрийте tracking URL як звичайний браузер і перевірте redirect із click_id.
  2. Відправте тестову форму та отримайте 201 зі статусом new.
  3. Переконайтеся, що buyer отримав payload лише один раз або дедуплікував retry.
  4. Надішліть callback valid, потім ftd згідно з вашим процесом.
  5. Перевірте lead, delivery logs, callback history і статистику в Admin.
  6. Повторіть той самий телефон і переконайтеся, що відповідь має статус duplicate.
Troubleshooting

Типові помилки

400

Немає campaign у tracking URL, невалідні поля intake або відсутні обов’язкові consent.

401/403

Неправильний токен, неактивна кампанія/buyer або campaign у body не відповідає токену.

404

Немає активної воронки, ID неправильний або buyer намагається змінити чужий lead.

409

Перехід між статусами суперечить дозволеній status machine.

click_id відсутній

Порожній/bot User-Agent або понад 60 кліків за хвилину з однієї IP — redirect відбудеться без запису кліку.

Готово до тесту?

Створіть конфігурацію в Admin і пройдіть чекліст одним тестовим лідом.

Відкрити Admin