Документация 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Список пассов
Параметры запроса
| Поле | Тип | Описание |
|---|---|---|
search | string | Поиск по названию или коду пасса |
status | string | active, used, expired, revoked |
limit | number | Размер страницы (по умолчанию 25, максимум 100) |
page | number | Номер страницы (по умолчанию 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Создание пасса
Тело запроса
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
type | string | Да | "single" или "multi" |
name | string | Нет | Понятное пользователю название |
note | string | Нет | Внутреннее примечание (не отображается на публичной странице) |
clientId | string | Нет | ID существующего клиента; имеет приоритет над clientName |
clientName | string | Нет | Автоматически создать или привязать клиента по имени |
maxUsages | number | Нет | Максимальное число сканирований для многоразовых пассов (null = без ограничений) |
validFrom | string | Нет | Дата в формате ISO 8601 (например, "2025-06-01T00:00:00Z") |
validUntil | string | Нет | Дата в формате ISO 8601 |
customFields | array | Нет | Массив объектов { "key": "...", "value": "...", "isPublic": true } |
templateId | string | Нет | ID шаблона оформления пасса |
locationId | string | Нет | 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Обновить пасс
Тело запроса
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
name | string | Нет | Обновить название |
note | string | Нет | Внутреннее примечание |
status | string | Нет | "active" или "revoked" |
maxUsages | number | null | Нет | Обновить лимит сканирований |
validFrom | string | null | Нет | Дата в формате ISO 8601 или null |
validUntil | string | null | Нет | Дата в формате ISO 8601 или null |
customFields | array | Нет | Заменяет все существующие поля |
Если передан 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Сканировать пасс
Тело запроса
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
code | string | Да | Код пасса (из QR-кода или публичной ссылки) |
locationId | string | Нет | Локация, в которой выполняется сканирование |
timezone | string | Нет | Часовой пояс 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Список клиентов
Параметры запроса
| Поле | Тип | Описание |
|---|---|---|
search | string | Поиск по имени, email или телефону |
limit | number | Размер страницы (по умолчанию 25, максимум 100) |
page | number | Номер страницы (по умолчанию 1) |
Ответ (200)
[
{
"id": "clx2...",
"name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890",
"notes": null,
"createdAt": "2025-06-01T12:00:00.000Z"
}
]POST/clientsСоздать клиента
Тело запроса
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
name | string | Да | Отображаемое имя клиента |
email | string | Нет | Адрес электронной почты |
phone | string | Нет | Номер телефона |
notes | string | Нет | Внутренние примечания |
Ответ (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Подписаться на вебхук
Тело запроса
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
event | string | Да | Одно из событий: pass.created, pass.scanned, pass.status_changed, client.created |
targetUrl | string | Да | 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. Возвращает последние записи указанного события после заданного момента времени.
Параметры запроса
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
event | string | Да | Одно из событий: pass.created, pass.scanned, pass.status_changed, client.created |
since | string | Нет | Дата в формате ISO 8601 — вернуть записи после этого момента (по умолчанию за последние 24 часа) |
limit | number | Нет | Максимальное число записей (по умолчанию 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);
