Конверсии
Методы для журнала конверсий - всего, что пришло по постбекам от партнёрок и из самого трафика (установки, открытия оффера). Здесь каждое событие видно отдельной строкой: с выплатой, посетителем и статусом отправки в Facebook. Что значат колонки и статусы в панели, см. раздел «Конверсии».
| Метод | Адрес | Что делает |
|---|---|---|
| GET | /conversions | Список конверсий за период |
| GET | /conversions/{id} | Одна конверсия и все визиты её посетителя |
| POST | /conversions/{id}/re-export | Отправить конверсию в Facebook ещё раз |
Замените ВАШ_КЛЮЧ и panel.example.com на свои.
Список конверсий
GET/conversions
Конверсии ваших PWA за период, свежие сверху.
| Параметр | Обязательно | Что фильтрует |
|---|---|---|
date_from, date_to | да | Период по дате получения события: ГГГГ-ММ-ДД, дни целиком по UTC, не длиннее месяца - как в разделе «Статистика» |
event[] | нет | Тип события, можно несколько: install, open_offer, registration, deposit, repeat_deposit, page_view |
export_status | нет | Статус отправки в Facebook: exported, failed, not_exported или open_offer |
pwa_id или pwa_ids[] | нет | Одно или несколько PWA |
split_id или split_ids[] | нет | Одно или несколько сплитов - конверсии с их потоков |
visitor_id | нет | Часть идентификатора посетителя |
page, per_page | нет | Страница и её размер: по умолчанию 100, максимум 200 |
curl -G "https://panel.example.com/api/conversions" \
-H "Authorization: Bearer ВАШ_КЛЮЧ" \
-H "Accept: application/json" \
--data-urlencode "date_from=2026-10-01" \
--data-urlencode "date_to=2026-10-05" \
--data-urlencode "event[]=deposit" \
--data-urlencode "event[]=repeat_deposit"<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer ВАШ_КЛЮЧ', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$page = 1;
do {
$response = $api->get('conversions', ['query' => [
'date_from' => '2026-10-01',
'date_to' => '2026-10-05',
'event' => ['deposit', 'repeat_deposit'],
'per_page' => 200,
'page' => $page,
]]);
$data = json_decode($response->getBody(), true)['data'];
foreach ($data['conversions'] as $conversion) {
echo "{$conversion['received_at']} {$conversion['pwa']['name']}: {$conversion['payout']} {$conversion['currency']}\n";
}
$page++;
} while ($data['meta']['current_page'] < $data['meta']['last_page']);# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer ВАШ_КЛЮЧ", "Accept": "application/json"}
page = 1
while True:
response = requests.get(f"{API}/conversions", headers=HEADERS, params={
"date_from": "2026-10-01",
"date_to": "2026-10-05",
"event[]": ["deposit", "repeat_deposit"],
"per_page": 200,
"page": page,
}, timeout=30)
data = response.json()["data"]
for conversion in data["conversions"]:
print(conversion["received_at"], conversion["pwa"]["name"], conversion["payout"], conversion["currency"])
if data["meta"]["current_page"] >= data["meta"]["last_page"]:
break
page += 1// Node.js 18+, без зависимостей; сохраните как script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer ВАШ_КЛЮЧ', Accept: 'application/json', 'Content-Type': 'application/json' };
for (let page = 1; ; page++) {
const params = new URLSearchParams({
date_from: '2026-10-01',
date_to: '2026-10-05',
per_page: 200,
page,
});
params.append('event[]', 'deposit');
params.append('event[]', 'repeat_deposit');
const response = await fetch(`${API}/conversions?${params}`, {
method: 'GET',
headers,
signal: AbortSignal.timeout(30000),
});
const { data } = await response.json();
for (const conversion of data.conversions) {
console.log(conversion.received_at, conversion.pwa.name, conversion.payout, conversion.currency);
}
if (data.meta.current_page >= data.meta.last_page) break;
}Ответ
{
"data": {
"meta": { "current_page": 1, "per_page": 200, "last_page": 1, "total": 1 },
"conversions": [
{
"id": "Rt5K",
"pwa": { "id": "WZAX", "name": "Promo EN", "icon_url": null },
"pwa_visitor_id": "7f3c2a9e-1b4d-4c8e-9a2f-5d6e7f8a9b0c",
"event": "deposit",
"raw_event": "ftd",
"payout": "45.00",
"currency": "USD",
"tid": "tx-55102",
"external_click_id": null,
"export_status": "failed",
"export_error": "[400] Malformed access token",
"exported_at": null,
"can_reexport": true,
"status": "scheduled",
"received_at": "05 Oct 2026 14:23",
"created_at": "05 Oct 2026 14:23",
"updated_at": "05 Oct 2026 14:23"
}
]
}
}| Поле | Что это |
|---|---|
pwa | PWA, которому засчитана конверсия; null, если PWA не определилось (такие строки видит только admin) |
pwa_visitor_id | Посетитель, по которому конверсия привязалась к визиту |
event | Тип события (список - в таблице параметров выше). null - партнёрка прислала статус, который панель не распознала: такое событие не считается в статистике и не уходит в Facebook |
raw_event | Статус ровно так, как его прислала партнёрка (ftd, sale, reg…) |
payout, currency | Выплата из постбека - строкой, как пришла |
tid | Идентификатор транзакции из постбека |
external_click_id | click_id из постбека, если партнёрка его передала |
export_status | Отправка в Facebook - см. ниже |
export_error | Ответ Facebook, если отправка не прошла |
exported_at | Когда событие ушло в Facebook |
can_reexport | Можно ли отправить ещё раз: нельзя у open_offer и у событий с event: null |
received_at | Когда событие пришло в панель - по нему фильтрует период |
status | Служебное поле, для скриптов значения не имеет |
export_status:
exported- Facebook принял событие.not_exported- событие ещё не отправлялось. Отправка идёт раз в минуту, но событие так и останетсяnot_exported, если у PWA нет пикселя с токеном, событию не назначено событие Facebook,eventравенnullили событие старше 3 дней.failed- Facebook отказал, причина - вexport_error. Если при этомexported_atпустой, ошибка временная (Facebook недоступен, лимит) - панель сама повторит отправку. Еслиexported_atзаполнен - отказ окончательный, нужен повторный экспорт после исправления.open_offer- служебное событие, в Facebook оно не отправляется вовсе.
Сумма за период
Чтобы посчитать выручку, складывайте payout по каждой currency отдельно - валюты в одну цифру не сводятся. Готовые суммы по валютам за период есть в разделе «Статистика» (total_deposits.amounts).
Фильтр export_status=open_offer показывает только открытия оффера, параметр event[] при нём не учитывается.
Ошибки
| Код | message | Когда |
|---|---|---|
422 | The date from field is required. (and 1 more error) | Не передан период; остальные ошибки периода - как в разделе «Статистика» |
422 | The selected event.0 is invalid. | Неизвестный тип события в event[] |
422 | The selected export status is invalid. | Неизвестный export_status |
422 | The per page field must not be greater than 200. | per_page больше 200 |
422 | Invalid pwa_id / Invalid split_id | Идентификатор PWA или сплита не распознан |
404 | Not found | PWA или сплит из фильтра чужой или не существует |
Одна конверсия
GET/conversions/{id}
Конверсия и до 100 последних визитов её посетителя: с какого домена и IP он приходил, с какими метками и какой визит принёс конверсию. Удобно, когда партнёрка спорит о конверсии или нужно найти креатив, который её принёс.
curl https://panel.example.com/api/conversions/Rt5K \
-H "Authorization: Bearer ВАШ_КЛЮЧ" \
-H "Accept: application/json"<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer ВАШ_КЛЮЧ', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$details = json_decode($api->get('conversions/Rt5K')->getBody(), true)['data'];
foreach ($details['visits'] as $visit) {
if ($visit['matched']) {
echo "Конверсию принёс визит {$visit['created_at']}, sub_1 = {$visit['subs']['sub_1']}\n";
}
}# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer ВАШ_КЛЮЧ", "Accept": "application/json"}
details = requests.get(f"{API}/conversions/Rt5K", headers=HEADERS, timeout=30).json()["data"]
for visit in details["visits"]:
if visit["matched"]:
print("Конверсию принёс визит", visit["created_at"], "sub_1 =", (visit["subs"] or {}).get("sub_1"))// Node.js 18+, без зависимостей; сохраните как script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer ВАШ_КЛЮЧ', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/conversions/Rt5K`, {
method: 'GET',
headers,
signal: AbortSignal.timeout(30000),
});
const { data } = await response.json();
for (const visit of data.visits) {
if (visit.matched) {
console.log('Конверсию принёс визит', visit.created_at, 'sub_1 =', visit.subs?.sub_1);
}
}Ответ
{
"data": {
"conversion": {
"id": "Rt5K",
"pwa": { "id": "WZAX", "name": "Promo EN", "icon_url": null },
"event": "deposit",
"payout": "45.00",
"currency": "USD",
"export_status": "failed",
"received_at": "05 Oct 2026 14:23"
},
"visits_count": 1,
"visits": [
{
"created_at": "05 Oct 2026 13:20",
"ip_address": "203.0.113.7",
"from_domain": "fit-tracker.example",
"country": "US",
"user_agent": "Mozilla/5.0 (Linux; Android 14)",
"offer_visitor_id": "aff-91822",
"offer_redirect_url": "https://offer.example/?sub1=fb-ua-creo1",
"subs": { "sub_1": "fb-ua-creo1", "sub_2": "adset-7" },
"third_party_tokens": null,
"is_unique": true,
"matched": true
}
]
}
}conversion - та же запись, что в списке (здесь сокращена). visits_count - сколько всего визитов у посетителя; в visits - не больше 100 последних, свежие сверху.
| Поле визита | Что это |
|---|---|
from_domain | Домен, на который пришёл посетитель |
offer_visitor_id, offer_redirect_url | Идентификатор посетителя у оффера и ссылка, на которую его отправили |
subs | Заполненные метки sub_1 … sub_20; null, если меток не было |
third_party_tokens | Остальные параметры из ссылки: fbclid, gclid и т.п. |
is_unique | Первый визит этого посетителя |
matched | Визит, который принёс конверсию: последний визит на тот же поток до получения события |
Если ни один визит не помечен matched, связанный визит не найден - чаще всего из-за потерянного pwa_visitor_id в постбеке.
Ошибки
| Код | message | Когда |
|---|---|---|
404 | Not found | Конверсия чужая или не существует |
Повторный экспорт
POST/conversions/{id}/re-export
Снимает отметку об отправке и ошибку, и ближайшая фоновая отправка (раз в минуту) попробует передать событие в Facebook ещё раз.
Пригодится, когда Facebook отказал из-за настройки, которую вы уже исправили: например, обновили токен пикселя. Сначала исправьте причину - иначе повтор получит ту же ошибку.
curl -X POST https://panel.example.com/api/conversions/Rt5K/re-export \
-H "Authorization: Bearer ВАШ_КЛЮЧ" \
-H "Accept: application/json"<?php
// composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
$api = new GuzzleHttp\Client([
'base_uri' => 'https://panel.example.com/api/',
'headers' => ['Authorization' => 'Bearer ВАШ_КЛЮЧ', 'Accept' => 'application/json'],
'timeout' => 30,
]);
$response = $api->post('conversions/Rt5K/re-export');
$conversion = json_decode($response->getBody(), true)['data']['conversion'];
echo "{$conversion['id']}: {$conversion['export_status']}" . PHP_EOL;# pip install requests
import requests
API = "https://panel.example.com/api"
HEADERS = {"Authorization": "Bearer ВАШ_КЛЮЧ", "Accept": "application/json"}
response = requests.post(f"{API}/conversions/Rt5K/re-export", headers=HEADERS, timeout=30)
conversion = response.json()["data"]["conversion"]
print(conversion["id"], conversion["export_status"])// Node.js 18+, без зависимостей; сохраните как script.mjs
const API = 'https://panel.example.com/api';
const headers = { Authorization: 'Bearer ВАШ_КЛЮЧ', Accept: 'application/json', 'Content-Type': 'application/json' };
const response = await fetch(`${API}/conversions/Rt5K/re-export`, {
method: 'POST',
headers,
signal: AbortSignal.timeout(30000),
});
const { data } = await response.json();
console.log(data.conversion.id, data.conversion.export_status);Ответ - конверсия после сброса, export_status у неё теперь not_exported:
Ответ
{
"data": {
"conversion": {
"id": "Rt5K",
"event": "deposit",
"export_status": "not_exported",
"export_error": null,
"exported_at": null,
"can_reexport": true
}
}
}Ошибки
| Код | message | Когда |
|---|---|---|
422 | Event cannot be re-exported | can_reexport: false - открытие оффера или событие с event: null |
404 | Not found | Конверсия чужая или не существует |
Только свежие конверсии
Фоновая отправка берёт события не старше 3 дней с момента получения (received_at). Повторный экспорт более старой конверсии сбросит её статус, но в Facebook она уже не уйдёт.
Чтобы переотправить все ошибки разом, возьмите список с export_status=failed и вызовите повторный экспорт для каждой строки - не чаще лимита запросов из «Введения в API».