Авторизация
Ключ выпускает владелец рекламного кабинета у себя в Kelvaro: «Интеграции» → «API для сторонних сервисов». Ключ привязан к одной учётной записи и открывает только её данные.
Он передаётся заголовком X-API-Key в каждом запросе:
curl -H "X-API-Key: kv_live_…" \
https://kelvaro.mtech.kz/api/v1/ping
Клиент может отозвать ключ в любой момент — обращения по нему
перестают проходить сразу, и приходит 401. Ключ
показывается клиенту один раз при выпуске: у нас хранится только его
отпечаток, поэтому «напомнить» ключ мы не сможем, его придётся
выпустить заново.
Проверка ключа. Ничьих данных не трогает — нужна, чтобы отличить «ключ не тот» от «запрос не тот».
Ответ
{
"ok": true,
"account_id": "u_8c93bc2c…",
"scope": "stats:read"
}
Ни почты, ни имени клиента здесь нет: для проверки ключа они не нужны.
Проекты клиента. Проект — это одна рекламная площадка; его
id нужен, чтобы запросить статистику. Отдаются только
активные.
Ответ
{
"data": [
{
"id": "p_1f4c…",
"name": "Стоматология — Meta",
"platform": "meta",
"platform_label": "Meta",
"status": "active"
}
]
}
Возможные platform: meta,
tiktok, google, yandex.
Статистика собирается по всем четырём; площадка без сборщика ответила
бы 501 no_collector.
Итоги и разбивка по кампаниям за период по одному проекту.
Параметры
| Параметр | Значение |
|---|---|
project |
id из /api/v1/projects. Если не передан —
первый проект клиента. all — сводка по всем
проектам сразу (все площадки); вместо кампаний — строки по
проектам. Если у проектов разные валюты, ответ
409 mixed_currency: суммы в разных валютах не
складываются. |
period |
day · week (по умолчанию) ·
month · year · custom |
sinceuntil |
Формат YYYY-MM-DD. Нужны только при
period=custom. |
Запрос
curl -H "X-API-Key: kv_live_…" \
"https://kelvaro.mtech.kz/api/v1/stats?project=p_1f4c…&period=month"
Ответ
{
"project": { "id": "p_1f4c…", "name": "…", "platform": "meta" },
"period": { "code": "month", "label": "Месяц",
"range": "1 — 30 июля 2026" },
"currency": "KZT",
"generated_at": "22.08.2026 11:40",
"totals": {
"spend": 184300.0, "impressions": 412887, "clicks": 6104,
"reach": 233410, "leads": 128, "purchases": 14, "revenue": 1260000.0,
"ctr": 1.4783, "cpc": 30.19, "cpl": 1439.84, "cpp": 13164.29,
"roas": 6.8367
},
"campaigns": [
{ "name": "Импланты — WhatsApp", "spend": 96100.0, "clicks": 3312,
"impressions": 210044, "reach": 130876, "leads": 81, "purchases": 0,
"revenue": 0.0, "ctr": 1.5768, "cpc": 29.01, "cpl": 1186.42,
"cpp": null, "roas": null }
]
}
Поля
| Поле | Тип | Смысл |
|---|---|---|
spend | число | Расход в валюте кабинета (currency) |
impressions | целое | Показы |
clicks | целое | Клики |
reach | целое | Охват — уникальные люди |
leads | целое | Целевые действия кабинета |
purchases | целое | Продажи по всем местам конверсии: сайт, приложение и
переписка, переданная через /api/v1/conversions |
revenue | число | Выручка по тем же продажам |
ctr | число | Кликабельность, в процентах |
cpc | число · null |
Цена клика; null, если кликов не было |
cpl | число · null |
Цена лида; null, если лидов не было |
cpp | число · null |
Цена продажи; null, если продаж не было |
roas | число · null |
Возврат на расход; null без выручки |
partial | есть или нет | Часть кабинетов не ответила — итог занижен |
cpl, cpp и roas приходят null,
когда делить не на что. Это не ноль: ноль читался бы как «лид
достался бесплатно». Подставлять вместо null нули на
своей стороне не стоит по той же причине.Числа приходят числами, а не строками: форматирование — дело
принимающей стороны. Валюта одна на весь ответ и лежит в
currency.
Продажи из переписки
POST /api/v1/conversions — сообщить, что переписка,
начатая с рекламы Meta, закончилась продажей. Мы отправляем событие в
Meta Conversions API от имени рекламного кабинета клиента; Meta
привязывает продажу к объявлению, она появляется в
purchases и revenue, а алгоритм начинает
искать похожих на тех, кто купил.
Нужен ключ с правом «Передавать продажи» — клиент отмечает
его при выпуске. Ключ только для чтения получит 403
forbidden.
Когда отправлять
Один раз, когда сделка оплачена. Повтор с тем же
event_id безопасен: уже принятое событие повторно в Meta
не уходит, ответ — "duplicate": true. Поэтому при
таймауте просто повторяйте запрос.
Тело запроса (JSON)
| Поле | Тип | Смысл |
|---|---|---|
event_id | строка | Обязательно. Номер сделки у вас, 1–100 символов
A-Z a-z 0-9 . _ : -. Ключ дедупликации. |
channel | строка | Обязательно: whatsapp, instagram
или messenger. |
ctwa_clid | строка | Для WhatsApp — из referral.ctwa_clid первого
входящего сообщения после клика по рекламе. Есть только у
номеров WhatsApp Business Platform (Cloud API); номер,
подключённый по QR-коду, его не получает. |
ig_sid | строка | Для Instagram — идентификатор собеседника (IGSID). |
psid | строка | Для Messenger — идентификатор собеседника на Странице (PSID). |
value | число | Сумма сделки. Обязательно для Purchase. |
currency | строка | ISO 4217, например KZT. Обязательно вместе с
value. |
event_name | строка | По умолчанию Purchase. Также:
LeadSubmitted, QualifiedLead,
InitiateCheckout, AddToCart,
ViewContent, OrderCreated,
OrderShipped, OrderDelivered,
OrderCanceled, OrderReturned,
CartAbandoned. |
event_time | целое | Unix-время события. По умолчанию — момент запроса. Не старше 7 дней: старые события Meta не принимает. |
project | строка | Проект из /api/v1/projects. Можно не
передавать, если у клиента канал настроен в одном проекте. |
Номер телефона, имя и текст переписки не передавайте: для привязки к рекламе они не нужны, и мы их не принимаем.
Запрос
curl -X POST -H "X-API-Key: kv_live_…" \
-H "Content-Type: application/json" \
"https://kelvaro.mtech.kz/api/v1/conversions" \
-d '{ "event_id": "deal-48213", "channel": "whatsapp",
"ctwa_clid": "ARAkLkA8rmlFeiCk…",
"value": 90000, "currency": "KZT" }'
Ответ
{ "status": "sent", "duplicate": false,
"event_id": "deal-48213", "project": "p_1f4c…" }
Ошибки этого метода
| HTTP | code | Что делать |
|---|---|---|
| 400 | invalid_event |
Поле field заполнено неверно; текст объясняет,
как надо. Повтор без исправления не поможет. |
| 400 | project_required |
Канал настроен в нескольких проектах — передайте
project. |
| 403 | forbidden |
У ключа нет права «Передавать продажи». Нужен новый ключ. |
| 409 | not_configured |
Для этого канала у клиента на нашей стороне не настроены продажи из переписки. Нужен клиент. |
| 409 | in_progress |
То же событие отправляется прямо сейчас. Повторить через минуту. |
| 409 | token_invalid |
Кабинет нужно переподключить на нашей стороне. Нужен клиент. |
| 502 | meta_rejected |
Meta не приняла событие; в message — её текст.
Сбой связи — повторить позже; ошибка данных — исправить. |
Ошибки
Тело ошибки всегда одной формы. Завязываться нужно на
code, а не на текст сообщения: текст мы можем уточнить, код
— нет.
{ "error": { "code": "unauthorized", "message": "…" } }
| HTTP | code | Что делать |
|---|---|---|
| 401 | unauthorized |
Ключ не передан, неверен или отозван. Повтор не поможет — попросить клиента выпустить новый. |
| 400 | bad_request |
Неверный period или даты. |
| 404 | not_found |
Такого проекта у этого ключа нет. |
| 404 | no_projects |
У клиента ещё нет активных проектов. |
| 409 | no_tokenno_accounts |
Рекламный кабинет не подключён на нашей стороне. Повтор не поможет, нужен клиент. |
| 409 | mixed_currency |
При project=all у проектов разные валюты —
запрашивать проекты по отдельности. |
| 501 | no_collector |
Отчёты по этой площадке ещё не поддержаны. |
| 502 | api |
Рекламная площадка не ответила. Повторить позже. |
Коротко: 4xx без api — повторять
бессмысленно, нужно действие человека; 502 — временно,
повторить позже.
Лимиты и кэш
Ответ /api/v1/stats кэшируется на нашей стороне на
5 минут. За ним стоит обращение в API рекламной площадки, у
которой есть собственная квота на токен клиента, — опрашивать чаще
смысла нет, вернутся те же цифры.
Разумный режим для интерфейса, где цифры показываются рядом с перепиской: запрашивать при открытии карточки и не чаще раза в 5 минут при обновлении.
Ответы отдаются с Cache-Control: no-store — это данные
конкретного клиента, и оседать в промежуточных кэшах они не должны.