СПРАВОЧНИК

Kelvaro API

Чтение статистики рекламных кампаний: расход, показы, клики, лиды, продажи и разбивка по кампаниям. Плюс одна запись — передача продаж из переписки в Meta (ниже), для неё нужен ключ с отдельным правом. Изменить рекламу по ключу нельзя. Базовый адрес — https://kelvaro.mtech.kz.

Авторизация

Ключ выпускает владелец рекламного кабинета у себя в Kelvaro: «Интеграции» → «API для сторонних сервисов». Ключ привязан к одной учётной записи и открывает только её данные.

Он передаётся заголовком X-API-Key в каждом запросе:

curl -H "X-API-Key: kv_live_…" \
     https://kelvaro.mtech.kz/api/v1/ping
Запросы только сервер-серверные. Ключ нельзя класть в код страницы: заголовок из браузера виден пользователю, а ключ — это доступ ко всей статистике клиента. Заголовков CORS мы намеренно не отдаём, поэтому из браузера запрос и не пройдёт.

Клиент может отозвать ключ в любой момент — обращения по нему перестают проходить сразу, и приходит 401. Ключ показывается клиенту один раз при выпуске: у нас хранится только его отпечаток, поэтому «напомнить» ключ мы не сможем, его придётся выпустить заново.

GET/api/v1/ping

Проверка ключа. Ничьих данных не трогает — нужна, чтобы отличить «ключ не тот» от «запрос не тот».

Ответ

{
  "ok": true,
  "account_id": "u_8c93bc2c…",
  "scope": "stats:read"
}

Ни почты, ни имени клиента здесь нет: для проверки ключа они не нужны.

GET/api/v1/projects

Проекты клиента. Проект — это одна рекламная площадка; его id нужен, чтобы запросить статистику. Отдаются только активные.

Ответ

{
  "data": [
    {
      "id": "p_1f4c…",
      "name": "Стоматология — Meta",
      "platform": "meta",
      "platform_label": "Meta",
      "status": "active"
    }
  ]
}

Возможные platform: meta, tiktok, google, yandex. Статистика собирается по всем четырём; площадка без сборщика ответила бы 501 no_collector.

GET/api/v1/stats

Итоги и разбивка по кампаниям за период по одному проекту.

Параметры

ПараметрЗначение
project id из /api/v1/projects. Если не передан — первый проект клиента. all — сводка по всем проектам сразу (все площадки); вместо кампаний — строки по проектам. Если у проектов разные валюты, ответ 409 mixed_currency: суммы в разных валютах не складываются.
period day · week (по умолчанию) · month · year · custom
since
until
Формат 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…" }

Ошибки этого метода

HTTPcodeЧто делать
400invalid_event Поле field заполнено неверно; текст объясняет, как надо. Повтор без исправления не поможет.
400project_required Канал настроен в нескольких проектах — передайте project.
403forbidden У ключа нет права «Передавать продажи». Нужен новый ключ.
409not_configured Для этого канала у клиента на нашей стороне не настроены продажи из переписки. Нужен клиент.
409in_progress То же событие отправляется прямо сейчас. Повторить через минуту.
409token_invalid Кабинет нужно переподключить на нашей стороне. Нужен клиент.
502meta_rejected Meta не приняла событие; в message — её текст. Сбой связи — повторить позже; ошибка данных — исправить.

Ошибки

Тело ошибки всегда одной формы. Завязываться нужно на code, а не на текст сообщения: текст мы можем уточнить, код — нет.

{ "error": { "code": "unauthorized", "message": "…" } }
HTTPcodeЧто делать
401unauthorized Ключ не передан, неверен или отозван. Повтор не поможет — попросить клиента выпустить новый.
400bad_request Неверный period или даты.
404not_found Такого проекта у этого ключа нет.
404no_projects У клиента ещё нет активных проектов.
409no_token
no_accounts
Рекламный кабинет не подключён на нашей стороне. Повтор не поможет, нужен клиент.
409mixed_currency При project=all у проектов разные валюты — запрашивать проекты по отдельности.
501no_collector Отчёты по этой площадке ещё не поддержаны.
502api Рекламная площадка не ответила. Повторить позже.

Коротко: 4xx без api — повторять бессмысленно, нужно действие человека; 502 — временно, повторить позже.

Лимиты и кэш

Ответ /api/v1/stats кэшируется на нашей стороне на 5 минут. За ним стоит обращение в API рекламной площадки, у которой есть собственная квота на токен клиента, — опрашивать чаще смысла нет, вернутся те же цифры.

Разумный режим для интерфейса, где цифры показываются рядом с перепиской: запрашивать при открытии карточки и не чаще раза в 5 минут при обновлении.

Ответы отдаются с Cache-Control: no-store — это данные конкретного клиента, и оседать в промежуточных кэшах они не должны.