Примеры запросов
Семь готовых запросов к API ActionPulse на curl — событие, пакет, связывание личности, список событий с курсором, отчёт, статус импорта и отметка релиза из CI/CD.
На этой странице
Каждый пример ниже — законченная команда: скопируйте, подставьте свои значения и выполните. Ответы приведены такими, какими их возвращает сервер, и к каждому запросу указано, что означает неуспешный ответ.
Два адреса и два вида ключей
Поверхностей две, и их легко перепутать: ошибка адреса выглядит как 404,
ошибка ключа — как 401.
| Что вы делаете | Адрес | Чем авторизуетесь |
|---|---|---|
| Отправляете события и связываете личности | https://YOUR_ACTIONPULSE_INGEST_HOST + путь /v1/… |
ключ проекта: серверный pp_sk_… или браузерный pp_bt_… |
| Читаете данные и управляете объектами | https://YOUR_ACTIONPULSE_HOST/api/v1 |
токен доступа пользователя |
| Ставите отметку релиза из CI/CD | https://YOUR_ACTIONPULSE_HOST/api/v1 |
отдельный CI/CD-токен проекта |
Адрес приёма событий показан на экране установки трекера; в собственной
установке он задаётся переменной PUBLIC_INGEST_URL. Как получить токен
доступа — в аутентификации, про разделение поверхностей —
в двух поверхностях.
1. Отправить одно событие
Событиям, которым нужно доверять — оплаты, заказы, регистрации, — место на
сервере. Тело всегда объект с массивом batch, даже для одного события.
curl
test -n "$ACTIONPULSE_SERVER_KEY" || { echo "ACTIONPULSE_SERVER_KEY is required" >&2; exit 1; }
curl -sS -X POST https://YOUR_ACTIONPULSE_INGEST_HOST/v1/track \
-H "Authorization: Bearer $ACTIONPULSE_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{"batch":[{
"event_id":"8b0d3f2e-1c4a-4f0e-9b1d-2a3c4d5e6f70",
"event":"order_paid",
"user_id":"YOUR_USER_ID",
"time":"2026-07-28T10:15:00Z",
"props":{"revenue":1990,"currency":"RUB"}
}]}'Node.js
import { randomUUID } from 'node:crypto';
const key = process.env.ACTIONPULSE_SERVER_KEY;
if (!key) throw new Error('ACTIONPULSE_SERVER_KEY is required');
const response = await fetch('https://YOUR_ACTIONPULSE_INGEST_HOST/v1/track', {
method: 'POST',
headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
batch: [
{
event_id: randomUUID(),
event: 'order_paid',
user_id: 'YOUR_USER_ID',
time: new Date().toISOString(),
props: { revenue: 1990, currency: 'RUB' },
},
],
}),
});
const result = await response.json();
if (response.status !== 202 || result.rejected.length > 0) {
console.error('track failed', response.status, result);
}Тот же запрос на Python и на Go — в отправке событий с бэкенда.
Ожидаемый ответ — 202 Accepted:
{ "accepted": 1, "n": 1, "rejected": [] }
Что означает неуспешный ответ: 401 — ключ отсутствует, обрезан, отозван или
истёк; 402 — нет подписки либо закончился пробный период;
403 credential_scope_forbidden — ключ не того типа или без нужного права;
413 — тело больше 256 КБ; 422 — неизвестное поле или невалидный JSON.
2. Отправить пакет событий
Пакет — до 500 элементов и до 256 КБ тела. Элементы
независимы: часть может быть принята, часть отклонена.
curl -sS -X POST https://YOUR_ACTIONPULSE_INGEST_HOST/v1/track \
-H "Authorization: Bearer $ACTIONPULSE_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{"context":{"app_version":"2026.7.3"},"batch":[
{"event_id":"1f0c9b7a-2d3e-4f50-8a61-b2c3d4e5f601",
"event":"order_created","user_id":"YOUR_USER_ID","props":{"total":1990}},
{"event_id":"1f0c9b7a-2d3e-4f50-8a61-b2c3d4e5f602",
"event":"Order Paid","user_id":"YOUR_USER_ID","props":{"revenue":1990}}
]}'
Ответ — 202 с отказом по второму элементу: имя события не
подходит под ^[a-z0-9_.]{1,128}$.
{ "accepted": 1, "n": 2, "rejected": [{ "i": 1, "code": "invalid_event" }] }
n — сколько элементов вы прислали, accepted — сколько принято, i — индекс
в присланном массиве. Что означает неуспешный ответ: 422 — превышение
500 элементов или пустой пакет, 413 — превышение 256 КБ.
Полный перечень кодов отказа элементов — в лимитах приёма.
3. Связать анонимного посетителя с пользователем
Вызов связывает накопленную анонимную историю с идентификатором пользователя и
дополнительно записывает событие identify с переданными свойствами.
curl -sS -X POST https://YOUR_ACTIONPULSE_INGEST_HOST/v1/identify \
-H "Authorization: Bearer $ACTIONPULSE_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"anon_id":"a3f1c0de-77a2-4f9b-9c1e-5d6e7f809a10",
"user_id":"YOUR_USER_ID",
"traits":{"plan":"growth"}
}'
Ожидаемый ответ:
{ "accepted": 1, "n": 1 }
Оба идентификатора обязательны и ограничены 256 символами: без
одного из них ответ 422. Тело здесь ограничено 64 КБ — при превышении 413.
Ответ 409 privacy_job_active означает, что в проекте выполняется удаление
данных субъекта: связывание временно закрыто, ответ помечен
details.retryable = true и его можно повторить позже.
Если нужна только связь, без события identify, используйте /v1/alias — он
принимает те же anon_id и user_id, но не поле traits, и отвечает
{"status":"ok"}. Браузерным токенам этот путь недоступен по замыслу: попытка
вернёт 403 credential_scope_forbidden. Чем повтор такого вызова отличается от
повтора события — в идемпотентности и повторах.
4. Получить список событий с постраничностью
Чтение идёт с токеном доступа и по курсору. limit — не больше 100; общего
количества записей API не отдаёт, признак «есть ещё» — это наличие
next_cursor.
curl
curl -sS -G https://YOUR_ACTIONPULSE_HOST/api/v1/projects/YOUR_PROJECT_ID/events \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
--data-urlencode "from=2026-07-01T00:00:00Z" \
--data-urlencode "to=2026-07-28T00:00:00Z" \
--data-urlencode "event=order_paid" \
--data-urlencode "limit=50"Node.js
const token = 'YOUR_ACCESS_TOKEN'; // получите его через вход и обновляйте по истечении
const base = 'https://YOUR_ACTIONPULSE_HOST/api/v1/projects/YOUR_PROJECT_ID/events';
let cursor;
do {
const query = new URLSearchParams({
from: '2026-07-01T00:00:00Z', to: '2026-07-28T00:00:00Z',
event: 'order_paid', limit: '50',
});
if (cursor) query.set('cursor', cursor);
const response = await fetch(`${base}?${query}`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) throw new Error(`events failed: ${response.status}`);
const page = await response.json();
for (const event of page.events) process(event);
cursor = page.next_cursor;
} while (cursor);Ответ — страница записей и курсор следующей страницы:
{
"events": [
{
"project_id": 12,
"event_id": "8b0d3f2e-1c4a-4f0e-9b1d-2a3c4d5e6f70",
"event_name": "order_paid",
"actor_id": "YOUR_USER_ID",
"user_id": "YOUR_USER_ID",
"anon_id": "",
"session_id": "",
"event_time": "2026-07-28T10:15:00Z",
"server_time": "2026-07-28T10:15:00.412Z",
"props": "{\"revenue\":1990,\"currency\":\"RUB\"}",
"url": "",
"referrer": "",
"utm_source": "",
"device_type": "",
"country": ""
}
],
"next_cursor": "eyJ0IjoiMjAyNi0wNy0yOFQxMDoxNTowMFoiLCJpIjoxfQ"
}
Запись содержит ещё utm_medium, utm_campaign, os и browser — полный
состав полей на сгенерированной странице раздела. Обратите
внимание: props приходит строкой с JSON внутри, а не объектом — его нужно
разбирать отдельно. Пустые строки в незаполненных полях — это норма, а не
потеря данных.
Курсор непрозрачный и привязан к области запроса: поменяли диапазон, фильтры или
проект — старый курсор недействителен, начинайте страницы заново. См.
постраничность. Что означает неуспешный ответ: 401 — токен
истёк; 404 — проекта нет либо он не в вашей организации (различить эти случаи
API намеренно не даёт); 408 — запрос не уложился в отведённое время, сузьте
диапазон; 422 — неверные параметры или курсор.
5. Запустить отчёт
Отчёты считаются на запрос, синхронно. Минимальное тело воронки — массив шагов.
curl -sS -X POST https://YOUR_ACTIONPULSE_HOST/api/v1/projects/YOUR_PROJECT_ID/queries/funnel \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"date_from":"2026-07-01",
"date_to":"2026-07-28",
"window_hours":168,
"count_by":"actors",
"steps":[{"event":"signup_completed"},{"event":"order_paid"}]
}'
Ответ — блок total с шагами и метаданные расчёта:
{
"total": {
"steps": [
{ "name": "signup_completed", "actors": 1840, "conv_prev": 1, "conv_first": 1,
"avg_seconds_to_step": 0 },
{ "name": "order_paid", "actors": 212, "conv_prev": 0.1152, "conv_first": 0.1152,
"avg_seconds_to_step": 43210 }
]
},
"meta": { "cached": false, "ms": 184 }
}
Даты — в формате YYYY-MM-DD и в часовом поясе проекта. meta.estimate
означает, что значение оценочное, а не точный подсчёт. Что означает неуспешный
ответ: 402 — доступ приостановлен, нужен действующий тариф; 403 — роль не
позволяет запуск либо возможность не входит в тариф; 408 — расчёт не уложился
в отведённое время; 422 — некорректное тело, например шаг без имени события.
6. Прочитать состояние задачи импорта
Импорт выполняется в фоне: запуск возвращает задачу, а её состояние читается
отдельным запросом. Опрашивать имеет смысл, пока статус pending, running
или cancel_requested.
JOB_ID=4821
curl -sS https://YOUR_ACTIONPULSE_HOST/api/v1/imports/$JOB_ID \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
{
"id": 4821,
"project_id": 12,
"type": "csv",
"status": "running",
"progress": 62,
"stats": { "accepted": 812340, "rejected": 118, "duplicates": 0 },
"checkpoint": { "rows": 800000 },
"created_at": "2026-07-28T09:41:02Z",
"updated_at": "2026-07-28T10:14:55Z"
}
Статусов ровно шесть: pending, running, cancel_requested, canceled,
done, failed. Секреты из настроек задачи (строка подключения, путь к файлу)
в ответе не возвращаются. checkpoint — безопасная точка продолжения:
отменённый CSV-импорт можно продолжить с неё, а не с начала. Что означает
неуспешный ответ: 401 — токен истёк; 404 — задачи нет либо она принадлежит
другой организации. Подробнее — в импортах.
7. Создать отметку релиза из CI/CD
Отметки релизов создаются только через API — в кабинете их можно смотреть, но не
создавать. Токен для CI/CD тоже выпускается запросом к API от имени
администратора проекта: он умеет ровно одно действие и не принимается приёмом
событий. Заголовок Idempotency-Key обязателен — повторный запуск шага сборки
не должен создавать вторую отметку.
test -n "$PP_RELEASE_TOKEN" || { echo "PP_RELEASE_TOKEN is required" >&2; exit 1; }
curl -sS -i -X POST \
https://YOUR_ACTIONPULSE_HOST/api/v1/projects/YOUR_PROJECT_ID/release-markers/deploy \
-H "Authorization: Bearer $PP_RELEASE_TOKEN" \
-H "Idempotency-Key: $CI_JOB_ID" \
-H "Content-Type: application/json" \
-d '{
"name":"web 2026.7.3",
"ts":"2026-07-28T10:20:00Z",
"version":"2026.7.3",
"env":"production",
"description":"Новая корзина",
"link":"https://example.com/ci/builds/4821"
}'
Первый запуск — 201 и созданная отметка:
{
"id": 913,
"project_id": 12,
"name": "web 2026.7.3",
"description": "Новая корзина",
"ts": "2026-07-28T10:20:00Z",
"source": "deploy",
"version": "2026.7.3",
"env": "production",
"link": "https://example.com/ci/builds/4821",
"author": { "kind": "service_account", "name": "ci-deploy" },
"created_at": "2026-07-28T10:20:01Z",
"updated_at": "2026-07-28T10:20:01Z"
}
Повтор с тем же ключом и тем же телом — 200, заголовок
Idempotency-Replayed: true и та же отметка. Это успех, а не ошибка: шаг сборки
должен считать 200 нормальным результатом.
Что означает неуспешный ответ: 409 — тот же ключ уже использован с другим
телом (например, пересчитанное ts); 422 — нет name или ts, передан
source (он выводится сервером) либо ключ короче 8 или длиннее 128 символов;
402 — доступ приостановлен; 403 — возможность не входит в тариф или
лицензию; 404 — проект не принадлежит токену; 504 — запрос не уложился в
отведённое время, повторяйте с тем же ключом.
Частые ошибки
- Путают адреса —
404там, где ждут202. - Серверный ключ подставляют в браузер, а браузерный токен — в серверный вызов.
- Считают
202приёмом всех событий и не читаютrejected. - Разбирают
propsиз списка событий как объект, хотя это строка. - Повторяют постраничное чтение старым курсором после смены фильтров.
- Пересчитывают
tsпри повторе шага CI/CD и получают409вместо200.
Если ответ не совпал с ожиданием, начните с кодов ошибок: конверт у всех ошибок один, и код в нём точнее, чем статус.