Документация API

Интегрируйте создание пассов в ваши приложения

Тарифы Pro, Business и Enterprise. REST API доступен на тарифах Pro, Business и Enterprise. На тарифах Free и Basic создание API-ключей недоступно. Посмотреть тарифы →

Содержание

Аутентификация

Все запросы к API требуют действующий API-ключ в заголовке Authorization. Создать ключ можно в разделе Панель управления → API (тариф Про, только для администраторов).

Authorization: Bearer 2pk_your_api_key_here

Базовый URL

https://your-domain.com/api/v1

Все указанные ниже пути отсчитываются от этого базового URL. CORS включён — предварительный запрос OPTIONS поддерживается на каждом эндпоинте.

Лимиты запросов

  • 30 запросов в минуту на API-ключ для /passes, /clients, /scan
  • 10 запросов в минуту на API-ключ для /webhooks и /triggers
  • Заголовки лимитов в ответах: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
  • При превышении лимита ответ 429 содержит заголовок Retry-After

Коды ошибок

КодОписание
400Некорректный запрос — неверные входные данные или тело JSON
401Не авторизован — API-ключ отсутствует или недействителен
403Доступ запрещён — достигнут лимит тарифа, не подключён тариф Про или превышена квота вебхуков
404Не найдено — ресурс не существует или принадлежит другой организации
429Слишком много запросов — превышен лимит
500Внутренняя ошибка сервера

Все ошибки возвращаются в формате JSON: { "error": "message" }

Эндпоинты

GET/passesСписок пассов

Параметры запроса

ПолеТипОписание
searchstringПоиск по названию или коду пасса
statusstringactive, used, expired, revoked
limitnumberРазмер страницы (по умолчанию 25, максимум 100)
pagenumberНомер страницы (по умолчанию 1)

Ответ (200)

[
  {
    "id": "clx1abc...",
    "code": "AbCd1234EfGh",
    "name": "VIP Guest",
    "type": "single",
    "status": "active",
    "usagesCount": 0,
    "maxUsages": null,
    "validFrom": null,
    "validUntil": "2025-12-31T23:59:59.000Z",
    "client": { "name": "John Doe", "email": "john@example.com" },
    "publicUrl": "https://your-domain.com/p/AbCd1234EfGh?token=...",
    "createdAt": "2025-06-01T12:00:00.000Z"
  }
]

POST/passesСоздание пасса

Тело запроса

ПолеТипОбязательноеОписание
typestringДа"single" или "multi"
namestringНетПонятное пользователю название
notestringНетВнутреннее примечание (не отображается на публичной странице)
clientIdstringНетID существующего клиента; имеет приоритет над clientName
clientNamestringНетАвтоматически создать или привязать клиента по имени
maxUsagesnumberНетМаксимальное число сканирований для многоразовых пассов (null = без ограничений)
validFromstringНетДата в формате ISO 8601 (например, "2025-06-01T00:00:00Z")
validUntilstringНетДата в формате ISO 8601
customFieldsarrayНетМассив объектов { "key": "...", "value": "...", "isPublic": true }
templateIdstringНетID шаблона оформления пасса
locationIdstringНетID привязываемой локации

Ответ (201)

{
  "id": "clx1abc...",
  "code": "AbCd1234EfGh",
  "name": "VIP Guest",
  "type": "single",
  "status": "active",
  "maxUsages": null,
  "usagesCount": 0,
  "validFrom": null,
  "validUntil": "2025-12-31T23:59:59.000Z",
  "publicUrl": "https://your-domain.com/p/AbCd1234EfGh?token=...",
  "createdAt": "2025-06-01T12:00:00.000Z"
}

GET/passes/:passIdПолучить пасс

Возвращает полные данные пасса, включая дополнительные поля и привязанного клиента.

Ответ (200)

{
  "id": "clx1abc...",
  "code": "AbCd1234EfGh",
  "name": "VIP Guest",
  "type": "single",
  "status": "active",
  "usagesCount": 0,
  "maxUsages": null,
  "validFrom": null,
  "validUntil": "2025-12-31T23:59:59.000Z",
  "fields": [
    { "key": "Full Name", "value": "John Doe", "isPublic": true }
  ],
  "client": {
    "id": "clx2...", "name": "John Doe", "email": "john@example.com", "phone": null
  },
  "publicUrl": "https://your-domain.com/p/AbCd1234EfGh?token=...",
  "createdAt": "2025-06-01T12:00:00.000Z",
  "updatedAt": "2025-06-01T12:00:00.000Z"
}

PATCH/passes/:passIdОбновить пасс

Тело запроса

ПолеТипОбязательноеОписание
namestringНетОбновить название
notestringНетВнутреннее примечание
statusstringНет"active" или "revoked"
maxUsagesnumber | nullНетОбновить лимит сканирований
validFromstring | nullНетДата в формате ISO 8601 или null
validUntilstring | nullНетДата в формате ISO 8601 или null
customFieldsarrayНетЗаменяет все существующие поля

Если передан customFields, все существующие поля заменяются. Изменение статуса вызывает вебхук pass.status_changed.

POST/passes/:passId/activateАктивировать пасс

Устанавливает статус active. Возвращает 400, если пасс уже активен. Вызывает pass.status_changed и уведомляет приложения кошельков.

Ответ (200)

{ "id": "clx1abc...", "code": "AbCd1234EfGh", "name": "VIP Guest", "status": "active" }

POST/passes/:passId/revokeОтозвать пасс

Устанавливает статус revoked. Возвращает 400, если пасс уже отозван. Вызывает pass.status_changed и уведомляет приложения кошельков.

Ответ (200)

{ "id": "clx1abc...", "code": "AbCd1234EfGh", "name": "VIP Guest", "status": "revoked" }

POST/scanСканировать пасс

Тело запроса

ПолеТипОбязательноеОписание
codestringДаКод пасса (из QR-кода или публичной ссылки)
locationIdstringНетЛокация, в которой выполняется сканирование
timezonestringНетЧасовой пояс IANA для лимитов по календарным дням (по умолчанию UTC)

Проверяет статус, срок действия и лимиты использования пасса. Записывает сканирование и вызывает pass.scanned. Пассы типа single переходят в статус used после первого успешного сканирования. Параметр locationId необязателен; указанная локация должна принадлежать этой организации. Название локации сохраняется в записи сканирования, поэтому её переименование или удаление не меняет историю. Параметр timezone необязателен и должен содержать часовой пояс IANA, например Europe/London. Если значение отсутствует, некорректно или содержит только смещение, используется UTC.

Ответ (200)

{
  "success": true,
  "result": "success",
  "pass": {
    "id": "clx1abc...",
    "code": "AbCd1234EfGh",
    "name": "VIP Guest",
    "type": "single",
    "status": "used",
    "usagesCount": 1,
    "maxUsages": null
  },
  "scan": {
    "id": "clx3...",
    "scannedAt": "2025-06-01T12:30:00.000Z",
    "location": { "id": "loc_123", "name": "Downtown Shop" }
  }
}

Достигнут лимит использования (200)

{
  "success": false,
  "result": "redemption_limit_reached",
  "code": "redemption_limit_reached",
  "message": "This pass has reached its usage limit.",
  "lastRedemptionAt": "2026-08-16T08:42:13.000Z",
  "nextRedemptionAt": "2026-08-16T22:00:00.000Z",
  "redemptionLimit": { "type": "daily", "limit": 2 },
  "timezone": "Europe/Podgorica"
}

При неудачной проверке возвращается 200 с success: false и одним из значений result: revoked, already_used, not_yet_valid, expired, max_usages_reached, redemption_limit_reached.

GET/clientsСписок клиентов

Параметры запроса

ПолеТипОписание
searchstringПоиск по имени, email или телефону
limitnumberРазмер страницы (по умолчанию 25, максимум 100)
pagenumberНомер страницы (по умолчанию 1)

Ответ (200)

[
  {
    "id": "clx2...",
    "name": "John Doe",
    "email": "john@example.com",
    "phone": "+1234567890",
    "notes": null,
    "createdAt": "2025-06-01T12:00:00.000Z"
  }
]

POST/clientsСоздать клиента

Тело запроса

ПолеТипОбязательноеОписание
namestringДаОтображаемое имя клиента
emailstringНетАдрес электронной почты
phonestringНетНомер телефона
notesstringНетВнутренние примечания

Ответ (201)

{
  "id": "clx2...",
  "name": "John Doe",
  "email": "john@example.com",
  "phone": null,
  "notes": null,
  "createdAt": "2025-06-01T12:00:00.000Z"
}

Вызывает вебхук client.created.

GET/clients/:clientIdПолучить клиента

Ответ (200)

{
  "id": "clx2...",
  "name": "John Doe",
  "email": "john@example.com",
  "phone": "+1234567890",
  "notes": "VIP customer",
  "createdAt": "2025-06-01T12:00:00.000Z"
}

GET/webhooksСписок вебхуков

Ответ (200)

[
  {
    "id": "wh_...",
    "event": "pass.scanned",
    "targetUrl": "https://example.com/hook",
    "active": true,
    "createdAt": "2025-06-01T12:00:00.000Z"
  }
]

POST/webhooksПодписаться на вебхук

Тело запроса

ПолеТипОбязательноеОписание
eventstringДаОдно из событий: pass.created, pass.scanned, pass.status_changed, client.created
targetUrlstringДаHTTPS-адрес для получения POST-запросов с данными

Для организации действует фиксированная квота вебхуков — её превышение возвращает 403. При наступлении событий подписки отправляют POST-запрос с JSON на targetUrl.

Ответ (201)

{
  "id": "wh_...",
  "event": "pass.scanned",
  "targetUrl": "https://example.com/hook",
  "active": true,
  "createdAt": "2025-06-01T12:00:00.000Z"
}

Данные события pass.scanned

{
  "event": "pass.scanned",
  "timestamp": "2026-08-14T12:30:00.000Z",
  "data": {
    "id": "scan_123",
    "passId": "pass_123",
    "code": "AbCd1234EfGh",
    "result": "success",
    "scannedAt": "2026-08-14T12:30:00.000Z",
    "location": {
      "id": "loc_123",
      "name": "Downtown Shop"
    },
    "scannedById": "user_123",
    "scannedByName": "Shop Operator",
    "scannedByEmail": "operator@example.com"
  }
}

Если локация не указана, location имеет значение null. Только успешные сканирования вызывают pass.scanned; неудачные попытки остаются в истории сканирований.

DELETE/webhooks/:webhookIdОтписаться от вебхука

Ответ (200)

{ "success": true }

GET/triggersОпрос триггеров

Резервный механизм опроса для Zapier. Возвращает последние записи указанного события после заданного момента времени.

Параметры запроса

ПолеТипОбязательноеОписание
eventstringДаОдно из событий: pass.created, pass.scanned, pass.status_changed, client.created
sincestringНетДата в формате ISO 8601 — вернуть записи после этого момента (по умолчанию за последние 24 часа)
limitnumberНетМаксимальное число записей (по умолчанию 50, максимум 100)

Структура зависит от события: для pass.created записи соответствуют ответу списка пассов с дополнительными полями fields, clientName, clientEmail, clientPhone; для pass.scanned каждая запись содержит сканирование с location: { id, name } | null; для pass.status_changed — пасс с changedAt; для client.created — данные клиента.

Примеры

curl — создание пасса

curl -X POST https://your-domain.com/api/v1/passes \
  -H "Authorization: Bearer 2pk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "single",
    "name": "VIP Guest",
    "validUntil": "2025-12-31T23:59:59Z",
    "customFields": [
      { "key": "Full Name", "value": "John Doe", "isPublic": true }
    ]
  }'

curl — сканирование пасса

curl -X POST https://your-domain.com/api/v1/scan \
  -H "Authorization: Bearer 2pk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "code": "AbCd1234EfGh", "locationId": "loc_123", "timezone": "Europe/London" }'

curl — подписка на вебхук

curl -X POST https://your-domain.com/api/v1/webhooks \
  -H "Authorization: Bearer 2pk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "pass.scanned",
    "targetUrl": "https://example.com/hook"
  }'

JavaScript (fetch)

const response = await fetch("https://your-domain.com/api/v1/passes", {
  method: "POST",
  headers: {
    "Authorization": "Bearer 2pk_your_api_key_here",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    type: "single",
    name: "VIP Guest",
    customFields: [
      { key: "Full Name", value: "John Doe", isPublic: true }
    ],
  }),
});

const data = await response.json();
console.log(data.publicUrl);