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

Интеграция Meta CPA с Keitaro: API и постбэк

Как подключить Meta CPA к Keitaro: лиды на metacpa.ru/create с хешем API-потока, subid в sub5, постбэк со status_text, таблица статусов и разбор ошибок…

Интеграция Meta CPA с Keitaro: API и постбэк

Meta CPA принимает лиды по API запросом на https://metacpa.ru/create: нужны хеш API-потока, числовой ID страны, имя, телефон и IP покупателя. Subid клика Keitaro передаётся в метке sub5, а статусы возвращаются постбэком с макросом {status_text}.

Сеть работает: на metacpa.ru и metacpa.com она называет себя международной нутра-сетью с 2013 года и прямым рекламодателем с эксклюзивными офферами. Ниже настройка на октябрь 2026 года, сверенная с кабинетом Meta CPA и документацией Keitaro.

Что даёт интеграция по API

  • Лид уходит в сеть с вашего лендинга, промо можно переводить, переверстывать и тестировать без кабинета сети.
  • Keitaro получает новый лид, холд, апрув, отказ и треш по каждому клику, поэтому ROI видно по связке, а не по офферу.
  • Мусорные заявки можно отсеять до отправки: пустые имена, короткие номера, повторы.

Что взять в кабинете Meta CPA

Поле APIЧто этоГде взять
flow_idхеш потокасоздайте поток с типом «API»: кабинет сгенерирует только хеш, по которому принимаются лиды с ваших лендингов
geoчисловой ID странысправочник ГЕО в разделе помощи кабинета
name, phone, ipданные покупателяформа лендинга и сервер
sub1…sub5пользовательские меткизаполняете сами, sub5 под subid Keitaro
Таблица прокручивается вбок

Набор обязательных полей совпадает с инструкцией сервиса WebJack по отправке заявок в metacpa.ru. Генератор кода страны metacpa.ru/city/helper из старых инструкций сейчас отдаёт 404, поэтому ID страны берите в кабинете или у менеджера.

Сервер сам подсказывает, чего не хватает. При проверке 1 октября 2026 года запрос без хеша вернул HTTP 422 и {"errors":{"flow_hash":["The flow hash field is required."]}}, а с неверным хешем "The flow hash not found". В ответе поле называется flow_hash, но значение из flow_id сервер принимает.

Форма на лендинге

Если лендинг загружен в Keitaro архивом, трекер подставит ID клика вместо {subid}, так работает локальный лендинг. Поле sub3 в примере хранит имя баера, его можно убрать.

<form action="api.php" method="post">
  <input type="text" name="name" placeholder="Nombre" required>
  <input type="tel" name="phone" placeholder="Teléfono" required>
  <input type="hidden" name="subid" value="{subid}">
  <input type="hidden" name="sub3" value="buyer1">
  <button type="submit">Pedir</button>
</form>

Для лендинга на своём хостинге допишите к его адресу в Keitaro ?subid={subid} и выводите значение через htmlspecialchars($_GET['subid'] ?? '', ENT_QUOTES) в index.php.

Обработчик api.php для Meta CPA

<?php
// api.php: принимает заявку с лендинга и отправляет её в Meta CPA
const API_URL   = 'https://metacpa.ru/create';
const FLOW_ID   = 'ХЕШ_ПОТОКА';   // хеш API-потока из кабинета Meta CPA
const GEO_ID    = 0;              // числовой ID страны из справочника ГЕО в кабинете
const BEHIND_CF = false;          // true, если домен лендинга за Cloudflare
const TIMEOUT   = 3;              // секунды: лимит Keitaro для локальных лендингов
const LOG_FILE  = __DIR__ . '/meta-leads.log';

if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
    http_response_code(405);
    exit;
}

$name  = trim((string)($_POST['name'] ?? ''));
$phone = preg_replace('/\D+/', '', (string)($_POST['phone'] ?? ''));
$subid = preg_replace('/[^A-Za-z0-9_-]/', '', (string)($_POST['subid'] ?? ''));

if ($name === '' || strlen($phone) < 7) {
    header('Location: error.php');
    exit;
}

$ip = $_SERVER['REMOTE_ADDR'] ?? '';
if (BEHIND_CF && !empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
    $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
}

$lead = [
    'flow_id' => FLOW_ID,
    'geo'     => GEO_ID,
    'name'    => $name,
    'phone'   => $phone,
    'ip'      => $ip,
    'sub5'    => $subid,   // subid клика Keitaro, вернётся в постбэке как {sub5}
    'sub3'    => (string)($_POST['sub3'] ?? ''),
];

$ch = curl_init(API_URL);
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => http_build_query($lead),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => TIMEOUT,
    CURLOPT_TIMEOUT        => TIMEOUT,
]);
$body = curl_exec($ch);
$code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
$resp = is_string($body) ? json_decode($body, true) : null;

// Ошибки валидации приходят с кодом 422: {"errors":{"поле":["текст"]}}
$ok = $code >= 200 && $code < 300 && is_array($resp)
    && !isset($resp['errors']) && !isset($resp['error']);

file_put_contents(LOG_FILE, date('c') . " http=$code subid=$subid "
    . substr((string)$body, 0, 300) . "\n", FILE_APPEND | LOCK_EX);

header('Location: ' . ($ok ? 'success.php' : 'error.php'));
exit;

До рабочего запуска подтвердите позитивный ответ API и ID заказа по документации кабинета: исправленное условие отклоняет некорректный JSON, но отсутствие errors само по себе ещё не подтверждает создание заказа. Журнал храните вне публичной папки и удаляйте из ответа персональные данные и секреты до записи. Заголовку Cloudflare доверяйте только при запросе от доверенного прокси.

Что важно в коде:

  • Хеш потока и ID страны лежат в PHP, а не в скрытых полях формы. Иначе любой посетитель увидит хеш в исходнике и сможет слать лиды в ваш поток.
  • subid клика уходит в sub5: так же устроен шаблон Meta CPA в Keitaro, и в постбэке значение вернётся как {sub5}.
  • Ответ 422 с ключом errors считается ошибкой, тело ответа пишется в meta-leads.log. Формат успешного ответа сеть публично не описывает, поэтому пример использует упрощённое условие: любой код 2xx без errors и error.
  • IP берётся из REMOTE_ADDR, заголовку Cloudflare скрипт верит только при BEHIND_CF = true.
  • Таймаут 3 секунды соответствует лимиту Keitaro для PHP-лендингов в трекере.

В старом варианте этого скрипта была опечатка curl_close($ch) вместо curl_close($process) и цикл, который копировал в запрос все поля из $_POST. Новый код отправляет только нужные поля.

Для success.php подойдёт готовая мультиязычная страница «Спасибо за заказ», для error.php страница ошибки на нескольких языках.

Постбэк Meta CPA в Keitaro

Шаг 1. Партнёрская сеть в Keitaro

  1. «Партнерские сети» → «Создать», шаблон Metacpa.ru.
  2. «Параметры для оффера»: sub5={subid}. Поле работает для офферов в потоке редиректом, при API-отправке subid передаёт api.php.
  3. Postback URL шаблона построен на числовом {status} (0, 1, 3). Мы советуем заменить его на текстовый {status_text}: так холд и треш не смешаются с новыми лидами и отказами.
Настройка постбеков в трекере кейтаро с metacpa
https://ВАШ_ТРЕКЕР/POSTBACK_KEY/postback?subid={sub5}&status={status_text}&payout={payout}&currency={currency}&tid={id}&lead_status=new,hold&sale_status=approve&rejected_status=reject&trash_status=trash&from=metacpa

Обучение статусов (lead_status, sale_status и другие) описано в документации Keitaro по postback. ID оффера и лендинга можно сохранить в клик, дописав &sub_id_21={offer_id}&sub_id_22={landing_id}: берите номера sub_id, которые не заняты в кампании.

Шаг 2. Постбэк в кабинете Meta CPA

  1. Глобальный постбэк задаётся в профиле, свой постбэк можно указать в настройках потока.
  2. В кабинете отдельные поля для событий: новый лид, принят или холд, отклонён, апселл и частичная заявка. Вставьте одну и ту же ссылку в поля нового, принятого и отклонённого лида: статус различит {status_text}.
  3. Если в форме есть выбор метода, ставьте GET: параметры останутся в ссылке и будут видны в логе Keitaro.
  4. Сохраните настройки.
Настройка постбеков в партнерке metacpa

Макросы Meta CPA: {id} (номер лида), {sub1}…{sub5}, {ip}, {status}, {status_text}, {price} (ставка по офферу), {payout} (фактическая выплата), {currency}, {offer_id}, {landing_id}, {prelanding_id}, {flow_id}, {datetime_create}, {create_timestamp}, {upsell_payout} и {click_id}.

Статусы Meta CPA и Keitaro

{status_text}{status}Что значитТип в Keitaro
new0новый лидLead
holdне указанопринят, выплата в холдеLead
approve1принятSale
reject3отклонёнRejected
trashне указанотрешTrash
Таблица прокручивается вбок

Если холд должен сразу считаться продажей, перенесите hold в sale_status. Как апрув и холд влияют на итоговый ROI, разобрано в статье про метрики арбитража.

Ошибки и где их искать

Где видноСообщениеЧто сделать
meta-leads.log422, The flow hash field is requiredхеш не передан: заполните FLOW_ID
meta-leads.log422, The flow hash not foundхеш неверный или поток не API-типа: скопируйте хеш заново
meta-leads.loghttp=0запрос не дошёл или превысил таймаут, проверьте исходящие соединения хостинга
Keitaro, логи постбэковClick for subid REPLACE not foundметка в ссылке не совпадает с той, куда api.php кладёт subid
Keitaro, логи постбэковConversion type not foundпришёл статус без обучения, например hold: добавьте его в lead_status
Keitaro, логи постбэковIncorrect postback codeключ в ссылке устарел, возьмите его в «Настройки» → «Postback URL»
Таблица прокручивается вбок

Лог входящих постбэков в Keitaro находится в «Обслуживание» → «Логи» → «Postbacks», расшифровка ошибок в разделе устранение проблем с postback.

Частые вопросы

Где взять хеш потока для API?

Создайте в кабинете поток с типом «API». Для такого потока Meta CPA генерирует только хеш, который и передаётся в flow_id.

Что передавать в geo?

Числовой ID страны из справочника ГЕО Meta CPA, а не ISO-код. Если не нашли справочник, запросите ID у менеджера.

Почему в постбэке лучше {status_text}, а не {status}?

Числовой статус различает только новый, принятый и отклонённый лид. Текстовый дополнительно передаёт hold и trash, и Keitaro разложит их по нужным типам конверсий.

Можно ли передать subid не в sub5?

Можно в любую из меток sub1…sub5, если в постбэке стоит та же метка. sub5 удобна тем, что её использует шаблон Keitaro.

Почему тестовый постбэк не создаёт конверсию?

В тестовых постбэках сети не передают реальный subid клика, это отдельно отмечено в документации Keitaro. Проверяйте связку настоящей заявкой по ссылке кампании.

Редакция BoostClicks

Материалы о настройке рекламы, трекинге и автоматизации: инструкции, примеры кода и расчёты с указанием условий проверки.