К содержанию
ActionPulse

Примеры запросов

Семь готовых запросов к 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.

Если ответ не совпал с ожиданием, начните с кодов ошибок: конверт у всех ошибок один, и код в нём точнее, чем статус.