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

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

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

Contents

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

All API requests require a valid API key sent in the Authorization header. API keys can be created in the Dashboard → API section (Pro plan, admin only).

Authorization: Bearer 2pk_your_api_key_here

Базовый URL

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

All endpoints below are relative to this base URL. CORS is enabled — preflight OPTIONS is supported on every endpoint.

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

  • 30 requests per minute per API key for /passes, /clients, /scan
  • 10 requests per minute per API key for /webhooks and /triggers
  • Rate limit headers in responses: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
  • When rate limited, a 429 response includes a Retry-After header

Коды ошибок

CodeDescription
400Bad Request — invalid input or JSON body
401Unauthorized — missing or invalid API key
403Forbidden — plan limit reached, not on Pro plan, or webhook quota exceeded
404Not Found — resource does not exist or belongs to another tenant
429Too Many Requests — rate limit exceeded
500Internal Server Error

All errors return JSON: { "error": "message" }

Эндпоинты

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

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

FieldTypeDescription
searchstringMatch by pass name or code
statusstringactive, used, expired, revoked
limitnumberPage size (default 25, max 100)
pagenumberPage number (default 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Создание пасса

Тело запроса

FieldTypeRequiredDescription
typestringYes"single" or "multi"
namestringNoHuman-readable label
notestringNoInternal note (not shown on public page)
clientNamestringNoAuto-create or attach client by name
maxUsagesnumberNoMax scan count for multi passes (null = unlimited)
validFromstringNoISO 8601 date (e.g. "2025-06-01T00:00:00Z")
validUntilstringNoISO 8601 date
customFieldsarrayNoArray of { "key": "...", "value": "...", "isPublic": true }
templateIdstringNoPass template ID for styling
locationIdstringNoLocation ID to attach

Ответ (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Получить пасс

Returns the full pass including custom fields and attached client.

Ответ (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Обновить пасс

Тело запроса

FieldTypeRequiredDescription
namestringNoUpdate label
notestringNoInternal note
statusstringNo"active" or "revoked"
maxUsagesnumber | nullNoUpdate scan limit
validFromstring | nullNoISO 8601 date or null
validUntilstring | nullNoISO 8601 date or null
customFieldsarrayNoReplaces all existing fields

When customFields is provided, all existing fields are replaced. Status change emits a pass.status_changed webhook.

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

Sets status to active. Returns 400 if already active. Emits pass.status_changed and notifies wallet apps.

Ответ (200)

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

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

Sets status to revoked. Returns 400 if already revoked. Emits pass.status_changed and notifies wallet apps.

Ответ (200)

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

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

Тело запроса

FieldTypeRequiredDescription
codestringYesThe pass code (from QR or public URL)
locationIdstringNoLocation where the scan is performed
timezonestringNoIANA timezone used for calendar-day limits (default UTC)

Validates the pass against status, validity dates, and usage limits. Records a scan and emits pass.scanned.single passes flip to used after the first successful scan.locationId is optional; when supplied, it must belong to this organization. The recorded location name is snapshotted so historical scans are unaffected by later renames or deletion.timezone is optional and should be an IANA timezone such as Europe/London. Missing, invalid, or offset-only values fall back to 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" }
  }
}

Usage limit reached (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"
}

On failure, response is 200 with success: false and result:revoked, already_used, not_yet_valid, expired, max_usages_reached, redemption_limit_reached.

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

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

FieldTypeDescription
searchstringMatch by name, email or phone
limitnumberPage size (default 25, max 100)
pagenumberPage number (default 1)

Ответ (200)

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

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

Тело запроса

FieldTypeRequiredDescription
namestringYesClient display name
emailstringNoEmail address
phonestringNoPhone number
notesstringNoInternal notes

Ответ (201)

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

Emits client.created webhook.

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Подписаться на вебхук

Тело запроса

FieldTypeRequiredDescription
eventstringYesOne of: pass.created, pass.scanned, pass.status_changed, client.created
targetUrlstringYesHTTPS URL receiving POST payloads

Tenant has a fixed webhook quota — exceeding it returns 403. Subscribed events POST a JSON payload to targetUrl.

Ответ (201)

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

pass.scanned payload

{
  "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 is null when no location was supplied. Only successful scans emit pass.scanned; unsuccessful attempts remain available in scan history.

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

Ответ (200)

{ "success": true }

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

Polling fallback used by Zapier. Returns recent items for the requested event since a given timestamp.

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

FieldTypeRequiredDescription
eventstringYesOne of: pass.created, pass.scanned, pass.status_changed, client.created
sincestringNoISO 8601 date — return items after this time (default last 24h)
limitnumberNoMax items (default 50, max 100)

Each event returns a different shape — for pass.created the items match the list-passes response with extra fields, clientName, clientEmail, clientPhone; for pass.scanned each item is a scan record with location: { id, name } | null; for pass.status_changed a pass with changedAt; for client.created the client record.

Примеры

curl — create pass

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 — scan pass

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 — subscribe webhook

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);