Integration guide
Покрокова інтеграція Citadel
Від створення buyer-а до перевіреного callback. Ідіть зверху вниз: кожен крок має результат, який варто перевірити перед наступним.
Один click_id з’єднує весь шлях
- Реклама веде не прямо на landing, а на tracking URL Citadel.
- Tracker створює клік і робить redirect на активну воронку з
click_id. - Landing зберігає
click_idі додає його до заявки. - Intake перевіряє токен кампанії, consent, дублікати й доступний cap.
- Citadel асинхронно надсилає лід buyer-у; buyer повертає статус callback-ом.
Створіть 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.
Скопіюйте готове посилання в Admin → Funnels
Після збереження Funnel Citadel автоматично створює її tracking URL з правильними ID кампанії та воронки.
- Відкрийте Admin → Traffic → Funnels.
- Знайдіть потрібну воронку.
- Натисніть «Копіювати» у колонці «Трекер». Повний URL уже міститиме домен, campaign і funnel.
- Вставте скопійоване посилання в рекламу замість прямого URL landing page.
- Кнопка «Тест» відкриває посилання й дозволяє одразу перевірити 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.
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 на вашому сервері.
Створіть 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;
Що саме замінити
YOUR_CITADEL_DOMAIN→ домен, де відкривається ваш Citadel.YOUR_CAMPAIGN_INTAKE_TOKEN→ відкрийте потрібну Campaign в Admin і скопіюйте її Intake Token.- У JavaScript вище змініть
PLіplна GEO та мову цього landing, якщо потрібно.
Готовий шлях: реклама → Citadel tracker → landing із click_id → форма → submit-lead.php → Citadel Lead Intake.
4.1 Як перевірити без здогадок
- Відкрийте tracking URL кампанії у звичайному браузері.
- Після redirect перевірте, що адреса landing містить
?click_id=.... - Заповніть форму та натисніть «Відправити».
- Має з’явитися повідомлення «Дякуємо! Заявку прийнято».
- Відкрийте Admin → Leads: новий лід повинен бути там із Click ID.
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.
Прийміть лід на стороні 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 секунд.
Поверніть статус ліда 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.
Пройдіть тест перед запуском трафіку
- Відкрийте tracking URL як звичайний браузер і перевірте redirect із
click_id. - Відправте тестову форму та отримайте
201зі статусомnew. - Переконайтеся, що buyer отримав payload лише один раз або дедуплікував retry.
- Надішліть callback
valid, потімftdзгідно з вашим процесом. - Перевірте lead, delivery logs, callback history і статистику в Admin.
- Повторіть той самий телефон і переконайтеся, що відповідь має статус
duplicate.
Типові помилки
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 відбудеться без запису кліку.
Готово до тесту?