Интеграция Affstar с Keitaro: lead API и постбэк
Affstar lead API и Keitaro: где взять ApiKey и хеш потока, обязательные поля, рабочий api.php, настройка постбэка и соответствие статусов сети и трекера…

Affstar принимает лиды через lead API: ваш лендинг отправляет POST-запрос в JSON на https://api.affstar.com/api/lead/create/publisher с ключом ApiKey и хешем потока. ID клика Keitaro уходит в поле sub_id1 и возвращается в постбэке, по нему трекер записывает конверсию и её статус.
Ниже по шагам: где взять ApiKey и хеш потока, какие поля обязательны, рабочий api.php, постбэк и таблица статусов. Поля и ответы API сверены с разделом «API» кабинета Affstar (его тексты загружаются вместе с кодом страницы my.affstar.com) и проверены запросом к API 1 октября 2026 года.
- Affstar в 2026 году
- Как работает связка Affstar и Keitaro
- Где взять ApiKey и хеш потока
- Affstar lead API: адрес, авторизация, поля
- Форма на лендинге
- PHP-обработчик api.php для Affstar
- Как проверить отправку
- Постбэк Affstar в Keitaro
- Шаг 1. Партнёрская сеть в Keitaro
- Шаг 2. Постбэк в кабинете Affstar
- Статусы Affstar и Keitaro
- Частые ошибки
- Частые вопросы
- Где взять API ключ Affstar?
- Какой адрес у Affstar lead API?
- Какие поля обязательны в API Affstar?
- Как узнать статус лида Affstar без постбэка?
- Почему Affstar не отправляет постбэк в Keitaro?
Affstar в 2026 году
- Сеть работает. affstar.com открывается, описание на сайте: «CPA-сеть и рекламодатель нового поколения», белые и нутра-офферы, приём трафика в том числе из ВК и Facebook. Регистрация есть на сайте, кабинет вебмастера на my.affstar.com.
- API на месте. Адрес приёма лидов прежний, на запрос с неверным ключом он отвечает
{"success":false,"errors":["Unauthorized"]}и кодом 401. - В кабинете есть всё для интеграции: раздел «Инструменты» с пунктами «Постбэки», «Лог постбэков», «Парковка домена» и «API».
Наш опыт 2024 года: топовые офферы, разные гео, адекватные менеджеры и выплаты без задержек. Из минусов были низкие ставки на старте, долгие согласования гарантов и всего остального. Чем Affstar отличается от других сетей и с какой начинать, мы разбирали в подборке CPA-сетей для новичков.
Как работает связка Affstar и Keitaro
- Клик приходит в Keitaro, трекер создаёт subid и показывает ваш лендинг.
- В форме лендинга Keitaro подставляет subid вместо макроса
{subid}. api.phpотправляет лид в Affstar, subid уходит вsub_id1.- Affstar присылает постбэк с
{sub_id1}и статусом: новый, подтверждённый, отклонён или треш. Keitaro записывает конверсию.
Трекер под такую схему мы сравнивали в статье Keitaro, Binom или AIO.
Где взять ApiKey и хеш потока
- Попросите доступ к API. Affstar предупреждает: «Убедитесь, что для вашего аккаунта разрешен прием лидов по API. Свяжитесь со своим аффилейт-менеджером».
- ApiKey. Откройте профиль
my.affstar.com/profile: кликните по аватару в шапке и выберите «Редактировать». Ключ лежит на вкладке безопасности, там же его можно показать, скопировать или перевыпустить. Если ключ перевыпустили, замените его и вapi.php. - Хеш потока. Создайте поток на оффер и скопируйте его хеш. Это UUID: на строку другого формата API ответит «This is not a valid UUID».
Affstar lead API: адрес, авторизация, поля
Запрос отправляется методом POST, данные в JSON. Ключ передаётся в заголовке ApiKey или параметром api_key в адресе. Максимальная длина subid и utm-меток 255 символов.
| Поле | Обязательно | Что передавать |
|---|---|---|
flow_uuid | да | хеш потока |
ip | да | IP посетителя, который оставил заявку |
country_code | да | двухбуквенный ISO-код страны |
phone | да | телефон |
name | да | имя |
user_agent | в описании среди обязательных | user agent посетителя |
sub_id1 | для постбэка | subid из Keitaro |
sub_id2, sub_id3 | нет | свои метки |
utm_source, utm_medium, utm_campaign, utm_content, utm_term | нет | utm-метки |
subaccount, referer, comment | нет | субаккаунт, источник перехода, комментарий |
В русской версии раздела country_code описан как двухбуквенный ISO-код, в английской как числовой. Обработчик ниже шлёт две буквы, как в русской версии. Если API вернёт ошибку по этому полю, уточните формат у менеджера.
Ответы по описанию в кабинете:
- лид создан:
{"success":true,"data":{"lead_uuid":"0de94314-…"}}; - ошибка в поле:
{"success":false,"errors":[{"text":"This is not a valid UUID.","field":"flow_uuid"}]}; - неверный ключ:
{"success":false,"errors":["Unauthorized"]}; - лид отклонён: невалидный номер или «The order has already been placed to this phone number», то есть заказ с этим телефоном уже в обработке.
Статус лида можно запросить и без постбэка: GET https://api.affstar.com/api/lead/get-status?lead_uuid=ID_ЛИДА с той же авторизацией. Для нескольких лидов есть /api/lead/get-statuses?lead_list_uuid[]=…. Статусы в ответе: new, approved, rejected, trash.
Форма на лендинге
Affstar советует для API-интеграции переименовать лендинг из html в php и убрать с него JS-скрипт сети. Главный файл назовите index.php, тогда Keitaro подставит макросы. Каждой форме поставьте method="post" и action="api.php" и добавьте скрытые поля:
<form action="api.php" method="post">
<input type="text" name="name" placeholder="Имя" required minlength="2" maxlength="100">
<input type="tel" name="phone" placeholder="Телефон" required>
<!-- ID клика Keitaro: трекер подставит его вместо {subid} -->
<input type="hidden" name="subid" value="{subid}">
<input type="hidden" name="referrer" value="{referrer}">
<!-- Affstar принимает utm-метки как есть, до 255 символов -->
<input type="hidden" name="utm_source" value="{utm_source}">
<input type="hidden" name="utm_campaign" value="{utm_campaign}">
<input type="hidden" name="utm_content" value="{utm_content}">
<button type="submit">Заказать</button>
</form>Если лендинг на своём хостинге, а не в Keitaro, допишите к его ссылке в трекере ?subid={subid}, а в форме читайте значение из адреса с экранированием:
<!-- ссылка на лендинг в Keitaro: https://ВАШ_ЛЕНДИНГ/?subid={subid} -->
<input type="hidden" name="subid" value="<?= htmlspecialchars($_GET['subid'] ?? '', ENT_QUOTES) ?>">PHP-обработчик api.php для Affstar
Заполните API_KEY, FLOW_UUID и COUNTRY. Нужен PHP 7.2 или новее с curl и mbstring.
<?php
// api.php: принимает заявку с формы лендинга и отправляет лид в Affstar по API
declare(strict_types=1);
// ===== Настройки =====
const API_URL = 'https://api.affstar.com/api/lead/create/publisher';
const API_KEY = 'API_KEY'; // my.affstar.com/profile: ApiKey (доступ к API включает менеджер)
const FLOW_UUID = 'FLOW_UUID'; // хеш потока: создайте поток и скопируйте его
const COUNTRY = 'ES'; // двухбуквенный ISO-код страны оффера
const SUCCESS_PAGE = 'success.php';
const ERROR_PAGE = 'error.php';
const HTTP_TIMEOUT = 3; // секунды: Keitaro ждёт от PHP локального лендинга не дольше 3 с
const DEDUP_TTL = 86400; // не отправлять тот же телефон повторно 24 часа
const DATA_DIR = __DIR__ . '/data';
function go(string $page, array $query = []): void
{
$query = array_filter($query, 'strlen');
header('Location: ' . $page . ($query ? '?' . http_build_query($query) : ''), true, 303);
exit;
}
function post_str(string $key, int $max = 255): string
{
$v = isset($_POST[$key]) && is_string($_POST[$key]) ? trim(strip_tags($_POST[$key])) : '';
return mb_substr($v, 0, $max);
}
function client_ip(): string
{
// За Cloudflare в REMOTE_ADDR адрес узла Cloudflare, реальный IP посетителя в CF-Connecting-IP
foreach (['HTTP_CF_CONNECTING_IP', 'REMOTE_ADDR'] as $k) {
$ip = $_SERVER[$k] ?? '';
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
}
return '';
}
function landing_url(): string
{
$https = ($_SERVER['HTTPS'] ?? '') === 'on' || ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https';
$host = preg_replace('/[^A-Za-z0-9.:-]/', '', $_SERVER['HTTP_HOST'] ?? '');
return ($https ? 'https' : 'http') . '://' . $host . '/';
}
function post_macro(string $key): string
{
// Keitaro подставляет значения макросов в URL-кодировке: пробел придёт как "+"
return mb_substr(trim(urldecode(post_str($key, 1000))), 0, 255);
}
function referer_url(): string
{
// Откуда пришёл посетитель: поле referrer формы ({referrer} в Keitaro), иначе адрес лендинга
foreach ([post_macro('referrer'), (string) ($_SERVER['HTTP_REFERER'] ?? '')] as $ref) {
if (filter_var($ref, FILTER_VALIDATE_URL)) {
return mb_substr($ref, 0, 500);
}
}
return landing_url();
}
function user_agent(): string
{
return mb_substr((string) ($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 255);
}
function compact_fields(array $fields): array
{
return array_filter($fields, static function ($v): bool {
return $v !== '';
});
}
function data_file(string $name): string
{
if (!is_dir(DATA_DIR) && @mkdir(DATA_DIR, 0750, true)) {
@file_put_contents(DATA_DIR . '/.htaccess', "Require all denied\n");
}
return DATA_DIR . '/' . $name;
}
function log_event(array $row): void
{
$line = json_encode(['time' => date('c')] + $row, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE);
@file_put_contents(data_file('leads-' . date('Y-m-d') . '.log'), $line . PHP_EOL, FILE_APPEND | LOCK_EX);
}
function mask_phone(string $digits): string
{
return substr($digits, 0, 3) . '***' . substr($digits, -2);
}
// Защита от дублей: в файле хранятся не номера, а их HMAC, чтобы файл не стал базой телефонов
function phone_seen(string $digits, bool $remember = false): bool
{
$fp = @fopen(data_file('phones.json'), 'c+');
if ($fp === false) {
return false; // нет прав на запись: лид не блокируем
}
flock($fp, LOCK_EX);
$list = json_decode((string) stream_get_contents($fp), true);
$list = is_array($list) ? $list : [];
$now = time();
foreach ($list as $k => $t) {
if ($now - (int) $t > DEDUP_TTL) {
unset($list[$k]);
}
}
$key = hash_hmac('sha256', $digits, API_KEY);
$seen = isset($list[$key]);
if ($remember) {
$list[$key] = $now;
}
ftruncate($fp, 0);
rewind($fp);
fwrite($fp, (string) json_encode($list));
flock($fp, LOCK_UN);
fclose($fp);
return $seen;
}
function http_post(string $url, string $body, array $headers): array
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => HTTP_TIMEOUT,
CURLOPT_TIMEOUT => HTTP_TIMEOUT,
]);
$answer = curl_exec($ch);
return [
'code' => (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE),
'body' => is_string($answer) ? $answer : '',
'error' => curl_error($ch),
];
}
// ===== Обработка заявки =====
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
go('index.php');
}
if (!function_exists('curl_init')) {
log_event(['result' => 'no_curl']);
go(ERROR_PAGE);
}
$name = post_str('name', 100);
$phone = (string) preg_replace('/[^\d+]/', '', post_str('phone', 32));
$digits = (string) preg_replace('/\D/', '', $phone);
$subid = post_str('subid', 100);
$subid = preg_match('/^[A-Za-z0-9._-]+$/', $subid) ? $subid : ''; // {subid} не подставился = пусто
$thanks = ['name' => $name, 'phone' => $phone, 'pxl' => (string) preg_replace('/\D/', '', post_str('pxl', 32))];
if (mb_strlen($name) < 2 || strlen($digits) < 7 || strlen($digits) > 15) {
go(ERROR_PAGE);
}
if (phone_seen($digits)) {
log_event(['result' => 'duplicate', 'phone' => mask_phone($digits), 'subid' => $subid]);
go(SUCCESS_PAGE, $thanks);
}
$lead = [
'flow_uuid' => FLOW_UUID,
'country_code' => COUNTRY,
'name' => $name,
'phone' => $phone,
'ip' => client_ip(),
'user_agent' => user_agent(),
'referer' => referer_url(),
'sub_id1' => $subid, // вернётся в постбэке макросом {sub_id1}
'utm_source' => post_macro('utm_source'),
'utm_medium' => post_macro('utm_medium'),
'utm_campaign' => post_macro('utm_campaign'),
'utm_content' => post_macro('utm_content'),
'utm_term' => post_macro('utm_term'),
];
$json = (string) json_encode(compact_fields($lead), JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
$res = http_post(API_URL, $json, [
'Content-Type: application/json',
'ApiKey: ' . API_KEY,
]);
$answer = json_decode($res['body'], true);
$ok = is_array($answer) && ($answer['success'] ?? false) === true;
log_event([
'result' => $ok ? 'sent' : 'error',
'phone' => mask_phone($digits),
'subid' => $subid,
'http' => $res['code'],
'curl' => $res['error'],
'answer' => mb_substr($res['body'], 0, 500),
]);
if (!$ok) {
go(ERROR_PAGE);
}
phone_seen($digits, true);
go(SUCCESS_PAGE, $thanks);Перед рабочим запуском. Проверка номера и запись после ответа API выполняются раздельно: одновременные запросы могут пройти оба. Для защиты от гонки нужна атомарная операция или поддерживаемый сетью ключ идемпотентности. Папку data держите вне публичного каталога: создаваемый .htaccess не защищает её в nginx. В журнал попадает ответ API, который может содержать персональные данные или секреты; маскируйте их до записи. Заголовку CF-Connecting-IP доверяйте только от проверенного прокси Cloudflare.
Что делает скрипт:
- принимает только POST, проверяет имя и телефон (от 7 до 15 цифр);
- отправляет лид в JSON с заголовком
ApiKey, как в примере кода из кабинета Affstar; - считает лид принятым только при
"success":true, при любой ошибке ведёт наerror.php, а текст ошибки пишет вdata/leads-ГГГГ-ММ-ДД.log; - не отправляет повторно тот же номер 24 часа и хранит для этого HMAC-хеши, а не сами номера. Почему дубли бьют по апруву, мы писали в статье про дубли лидов;
- ждёт ответ не дольше 3 секунд: таково требование Keitaro к PHP локальных лендингов;
- за Cloudflare берёт IP посетителя из заголовка CF-Connecting-IP, а не адрес узла Cloudflare.
Старый скрипт из прошлой редакции статьи вёл на страницу «спасибо» только при success, но хранил телефоны открытым текстом в leads.txt и брал IP только из REMOTE_ADDR. Новый код мы проверили против локальной заглушки API в восьми сценариях и против настоящего API Affstar с неверным ключом. Ответ 401 скрипт обработал как ошибку и записал в журнал. Страницы для редиректа: «Спасибо за заказ» и страница ошибки.
Как проверить отправку
- Загрузите архив с
index.php,api.php,success.phpиerror.phpв Keitaro как локальный оффер и добавьте его в тестовую кампанию без фильтров. - Откройте ссылку кампании и проверьте в коде страницы, что в поле
subidстоит ID клика. - Отправьте форму. В журнале должна появиться строка
"result":"sent"и ответ сlead_uuid. - Найдите лид в «Детализации» кабинета Affstar: в колонке subid1 должен стоять ID клика.
Постбэк Affstar в Keitaro
Шаг 1. Партнёрская сеть в Keitaro
В Keitaro откройте «Партнерские сети» и нажмите «Создать». Если шаблона Affstar в списке нет, заполните поля вручную, как на скриншоте 2024 года: название Affstar, параметры для оффера sub_id1={subid} и Postback URL.

https://ВАШ_ДОМЕН/POSTBACK_KEY/postback?subid={sub_id1}&payout={amount}¤cy={currency}&status={status}&lead_status=new&sale_status=approved&rejected_status=rejected,trashВАШ_ДОМЕН и POSTBACK_KEY возьмите в «Настройки», «Postback URL». Макросы {sub_id1}, {amount}, {currency} и {status} взяты из шаблона 2024 года. В открытом описании API их нет, поэтому сверьте их со списком на странице создания постбэка в кабинете.
Шаг 2. Постбэк в кабинете Affstar
- Откройте «Инструменты», «Постбэки» и нажмите «Создать постбэк».
- Отметьте «Активный» и «Глобальный постбэк». Если глобальный не нужен, выберите потоки.
- Метод запроса: GET.
- Отметьте статусы «Новый», «Подтвержденный», «Отклонен» и «Трэш» и задайте им названия
new,approved,rejectedиtrash. - Вставьте ссылку из Keitaro в поле URL и нажмите «Сохранить».

В той же форме есть «Повторная отправка»: постбэк уйдёт ещё раз, если вознаграждение изменится после апрува. Ещё там есть опция «Отключить передачу постбэка второго звонка». Все отправки видны в «Инструменты», «Лог постбэков»: дата, ID лида, статус, код ответа трекера и URL. Если код ответа не 200, ищите причину в логе Keitaro.
Статусы Affstar и Keitaro
| Статус в Affstar | Название в постбэке | Конверсия в Keitaro |
|---|---|---|
| Новый | new | Lead, доход в холде |
| Подтвержденный | approved | Sale |
| Отклонен | rejected | Rejected |
| Трэш | trash | Rejected |
Названия статусов в Affstar можно задать любые. Если назвать их сразу lead, sale и rejected, обучение в ссылке Keitaro не понадобится. Чтобы видеть треш отдельной колонкой, в Keitaro есть тип конверсии Trash и параметр trash_status.
Частые ошибки
- «Unauthorized». Неверный ApiKey, ключ перевыпустили или аккаунту не открыт приём лидов по API.
- «This is not a valid UUID» в поле flow_uuid. Вместо хеша потока вставлен ID оффера или ссылка целиком.
- Заказ с этим телефоном уже в обработке. Повторная заявка: Affstar её не примет. Проверьте, не отправляет ли форма лид дважды.
- В Keitaro «Click for subid REPLACE not found». В
sub_id1не ушёл ID клика. Проверьте полеsubidв форме и что лендинг открыт через трекер. - «Conversion type not found». Названия статусов в Affstar не совпадают с обучением в ссылке. Разбор кодов ошибок есть в справке Keitaro.
- Тестовый постбэк не создал конверсию. В тестовом постбэке нет настоящего subid, проверяйте реальным лидом.
Бонус из старой редакции: архив лендинга Keto-Matcha Blue ES, который мы в 2024 году активно лили на Affstar. В архиве только HTML, обработчик из статьи нужно добавить самому. Жив ли оффер сейчас, уточните у менеджера.
Частые вопросы
Где взять API ключ Affstar?
В профиле my.affstar.com: аватар в шапке, «Редактировать», ApiKey. Приём лидов по API для аккаунта включает менеджер.
Какой адрес у Affstar lead API?
https://api.affstar.com/api/lead/create/publisher, метод POST, тело в JSON, ключ в заголовке ApiKey.
Какие поля обязательны в API Affstar?
flow_uuid, ip, country_code, phone и name. Для постбэка добавьте sub_id1 с ID клика.
Как узнать статус лида Affstar без постбэка?
GET-запросом на /api/lead/get-status?lead_uuid=… с тем же ключом. Ответ вернёт new, approved, rejected или trash.
Почему Affstar не отправляет постбэк в Keitaro?
Откройте «Лог постбэков» в кабинете. Если записей нет, постбэк не активен или не привязан к потоку, если есть код ошибки, смотрите лог постбэков в Keitaro.



