Обновлено: 16.10.2026
Парковка
Парковочные билеты на купленные места и фактические проезды от парковочной системы клуба: импорт покупки в CRM, проверка доступа на точке, въезды и выезды, в том числе накопленные без связи.
Охват раздела
| Область | В разделе | Комментарий |
|---|---|---|
Справочники парковок, зон, мест и точек | да | Только чтение |
Импорт купленных парковочных билетов | да | Основной сценарий интеграции |
Связь с клиентом, заказом и мероприятием | да | По идентификаторам CRM или источника |
Блокировка, снятие блокировки, отмена | да | Управляемый статус билета |
Проверка доступа | да | Без побочных эффектов |
Въезды, выезды, отказы, офлайн-пачки | да | Включая опоздавшие события |
Эквайринг и возврат денег | нет | Приходят подтверждённые суммы источника |
Открытие шлагбаума | нет | Проверка доступа ничего не открывает |
Продажа и резерв места до оплаты | нет | В CRM попадает купленное место |
Редактирование справочников парковок | нет | Ведётся на стороне парковки |
PDF-билет, письма, вебхуки, перевыпуск QR | нет | Решаются вне этого раздела |
Соглашения
| Правило | Как в API |
|---|---|
Транспорт | HTTPS, JSON в UTF-8, поля в snake_case |
Источник | Клуб и source_system берутся из токена, не из тела |
Время | RFC 3339 с часовым поясом, в базе UTC |
Деньги | Целые минимальные единицы: 150000 = 1500 RUB |
Идемпотентность | Idempotency-Key на каждый изменяющий вызов, хранится от 7 суток |
Версия записи | If-Match: "3"; расхождение — 412, нет заголовка — 428 |
Страницы | data + meta.next_cursor; limit до 100, по умолчанию 50 |
Лимиты | 300 запросов в минуту на чтение, 120 на запись, 600 на проверку доступа |
Неизвестные поля | 422; свои данные передаются в metadata до 16 КБ |
Две пары полей легко перепутать. version — версия записи в CRM для If-Match, source_version — версия снимка у источника. occurred_at — когда проезд произошёл, received_at — когда платформа его приняла; второе не заменяет первое.
| Право | Где нужно |
|---|---|
parking:read | Все методы GET |
parking:tickets:write | Создание, импорт, правка билета, suspend, resume, cancel |
parking:passages:write | POST /passages и POST /passages/batch |
parking:access:check | POST /access/check |
parking:credentials:read | Поле access_code в ответе, дополнительно к остальным правам |
Обзор методов
| Метод | Путь | Описание |
|---|---|---|
GET | /integrations/parking/lots | Справочник парковок |
GET | /integrations/parking/lots/{parking_lot_id}/spaces | Места парковки |
PUT | /integrations/parking/tickets/external/{external_id} | Занести купленное место |
POST | /integrations/parking/tickets | Создать парковочный билет |
GET | /integrations/parking/tickets | Список билетов |
GET | /integrations/parking/tickets/{ticket_id} | Карточка билета |
GET | /integrations/parking/tickets/{ticket_id}/status | Компактный статус билета |
PATCH | /integrations/parking/tickets/{ticket_id} | Исправить билет |
POST | /integrations/parking/tickets/{ticket_id}/suspend | Заблокировать билет |
POST | /integrations/parking/tickets/{ticket_id}/resume | Снять блокировку |
POST | /integrations/parking/tickets/{ticket_id}/cancel | Отменить билет |
POST | /integrations/parking/access/check | Проверить доступ |
POST | /integrations/parking/passages | Передать проезд |
POST | /integrations/parking/passages/batch | Передать пачку проездов |
GET | /integrations/parking/passages | История проездов |
Справочник парковок
GET /integrations/parking/lotsПарковки, доступные токену интеграции, вместе с зонами и точками проезда. Во всех остальных методах используются ID отсюда: свои идентификаторы парковок и шлагбаумов не принимаются.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
status | query | string | нет | Фильтр по состоянию парковки: active, closed, maintenance. |
limit | query | integer | нет | Размер страницы, от 1 до 100. По умолчанию — 50. |
cursor | query | string | нет | Значение meta.next_cursor из предыдущего ответа. |
curl "https://api.virazh.io/v1/integrations/parking/lots?status=active" \
-H "Authorization: Bearer sk_test_51H..."{
"data": [
{
"id": "10000000-0000-4000-8000-000000000001",
"external_id": "lot-north",
"name": "Северная парковка",
"address": "ул. Спортивная, 1",
"timezone": "Europe/Moscow",
"capacity": 420,
"status": "active",
"zones": [
{ "id": "20000000-...-0001", "name": "Зона A" }
],
"gates": [
{
"id": "30000000-...-0001",
"name": "Въезд с ул. Спортивной",
"direction": "entry"
}
]
}
],
"meta": { "next_cursor": null, "has_more": false }
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data[].capacity | integer | Сколько мест парковки можно продать всего — это не текущий остаток свободных. |
data[].timezone | string | Часовой пояс парковки в формате IANA: по нему клуб читает сроки действия билетов. |
data[].zones | array<Zone> | Зоны парковки: id, external_id, name. |
data[].gates | array<Gate> | Точки проезда: id, external_id, name и direction — entry, exit или both. |
meta.next_cursor | string | null | Курсор следующей страницы; null — страниц больше нет. |
Места парковки
GET /integrations/parking/lots/{parking_lot_id}/spacesНумерованные места с типом, пригодностью и оценкой занятости. Метод только читает справочник: место он не резервирует.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
parking_lot_id | путь | uuid | да | Парковка из справочника. |
zone_id | query | uuid | нет | Только места указанной зоны. |
status | query | string | нет | Пригодность места: active, blocked, maintenance. |
limit | query | integer | нет | Размер страницы, от 1 до 100. По умолчанию — 50. |
curl "https://api.virazh.io/v1/integrations/parking/lots/10000000-0000-4000-8000-000000000001/spaces?status=active" \
-H "Authorization: Bearer sk_test_51H..."{
"data": [
{
"id": "10000000-0000-4000-8000-000000000042",
"external_id": "A-042",
"parking_lot_id": "10000000-...-0001",
"zone_id": "20000000-...-0001",
"number": "A-042",
"type": "standard",
"status": "active",
"occupancy": "free",
"occupancy_updated_at": "2026-10-20T18:05:00Z"
}
],
"meta": { "next_cursor": null, "has_more": false }
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data[].type | string | Категория места: standard, accessible, vip, ev. |
data[].status | string | Пригодность места к использованию: active, blocked, maintenance. Не путать с занятостью. |
data[].occupancy | string | Оценка фактической занятости по проездам: free, occupied, unknown. |
data[].occupancy_updated_at | datetime | null | Когда занятость подтверждалась последний раз; null — сведений нет, значение occupancy доверять нельзя. |
Занести купленное место
PUT /integrations/parking/tickets/external/{external_id}Основной метод интеграции: заносит купленный билет и обновляет его при повторной синхронизации. Запись ищется по паре «источник + external_id», поэтому повторная выгрузка заказа не создаёт второй билет. Передаётся полный снимок импортируемых полей: пропущенные необязательные поля очищаются.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
external_id | путь | string | да | ID парковочного билета в системе-источнике, до 128 символов. Обязан совпадать с полем external_id в теле. |
Idempotency-Key | заголовок | uuid | да | Ключ идемпотентности запроса. Тот же ключ с другим телом вернёт 409 IDEMPOTENCY_CONFLICT. |
source_version | тело | integer | да | Монотонно возрастающая версия записи у источника, от 1. Меньшая версия — 409 STALE_SOURCE_VERSION, та же версия с другим снимком — 409 SOURCE_VERSION_CONFLICT. |
external_order_id | тело | string | да | Заказ у источника. Один заказ может содержать несколько парковочных билетов. |
external_order_line_id | тело | string | да | Строка парковочной покупки внутри заказа — именно по ней сумма учитывается один раз. |
parking_lot_id | тело | uuid | да | Парковка из справочника. |
parking_space_id | тело | uuid | нет | Конкретное место. Без него билет даёт право на любое свободное место в указанной зоне или на парковке. |
valid_from | тело | datetime | да | Начало действия включительно. |
valid_until | тело | datetime | да | Конец действия исключительно; строго позже valid_from. |
max_entries | тело | integer | null | да | Лимит успешных въездов: 1 — разовый билет, null — без ограничения. Поле обязательно, подразумеваемого значения нет. |
payment | тело | object | да | Подтверждённые источником сведения об оплате: status, amount_minor, currency, paid_minor, refunded_minor, paid_at. Деньги этот метод не списывает и не возвращает. |
customer | тело | object | нет | Покупатель: crm_user_id либо external_id, name, email, phone. Покупка без профиля допустима. |
vehicle | тело | object | нет | Автомобиль: plate_number и country_code обязательны внутри объекта. Можно не передавать, если доступ только по QR-коду. |
event_id | тело | integer | нет | Мероприятие CRM, к которому относится парковка. |
curl -X PUT "https://api.virazh.io/v1/integrations/parking/tickets/external/parking-ticket-10001" \
-H "Authorization: Bearer sk_test_51H..." \
-H "Idempotency-Key: 40000000-0000-4000-8000-000000000001" \
-H "Content-Type: application/json" \
-d '{
"external_id": "parking-ticket-10001",
"source_version": 1,
"external_order_id": "order-5001",
"external_order_line_id": "line-1",
"customer": { "external_id": "customer-101" },
"parking_lot_id": "10000000-0000-4000-8000-000000000001",
"parking_space_id": "10000000-0000-4000-8000-000000000042",
"vehicle": { "plate_number": "А123АА777", "country_code": "RU" },
"valid_from": "2026-10-20T16:00:00+03:00",
"valid_until": "2026-10-20T23:00:00+03:00",
"max_entries": 1,
"payment": {
"status": "paid",
"amount_minor": 150000,
"currency": "RUB",
"paid_minor": 150000,
"refunded_minor": 0,
"paid_at": "2026-10-15T14:30:00+03:00"
}
}'{
"data": {
"id": "20000000-0000-4000-8000-000000000001",
"number": "PARK-000001",
"external_id": "parking-ticket-10001",
"source_version": 1,
"source_system": "parking-vendor",
"status": "active",
"effective_status": "valid",
"occupancy_status": "outside",
"entries_used": 0,
"entries_remaining": 1,
"crm_user_id": 4821,
"crm_order_id": 91045,
"version": 1,
"created_at": "2026-10-15T11:30:02Z",
"updated_at": "2026-10-15T11:30:02Z"
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data.id | uuid | ID билета в CRM — им оперируют остальные методы. |
data.number | string | Читаемый номер билета, например PARK-000001. |
data.effective_status | string | Вычисляемое право на новый въезд: valid, not_paid, not_started, suspended, cancelled, refunded, expired, exhausted. |
data.version | integer | Версия записи CRM для If-Match. Это не source_version источника. |
data.access_code | string | Код для QR или штрихкода, выданный сервером. Приходит только с правом parking:credentials:read и не передаётся в URL. |
Создать парковочный билет
POST /integrations/parking/ticketsСоздаёт билет, когда внешняя система не ведёт синхронизацию по external_id. Одно право парковки — один билет. Повтор существующего external_id даёт 409: для обновления нужен PUT.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
Idempotency-Key | заголовок | uuid | да | Ключ идемпотентности; повтор с тем же ключом вернёт исходный ответ. |
body | тело | ticket_input | да | Тот же состав полей, что и у метода «Занести купленное место». |
curl -X POST "https://api.virazh.io/v1/integrations/parking/tickets" \
-H "Authorization: Bearer sk_test_51H..." \
-H "Idempotency-Key: 40000000-0000-4000-8000-000000000011" \
-H "Content-Type: application/json" \
-d '{
"external_id": "parking-ticket-10002",
"source_version": 1,
"external_order_id": "order-5001",
"external_order_line_id": "line-2",
"parking_lot_id": "10000000-0000-4000-8000-000000000001",
"zone_id": "20000000-0000-4000-8000-000000000001",
"valid_from": "2026-10-20T16:00:00+03:00",
"valid_until": "2026-10-20T23:00:00+03:00",
"max_entries": null,
"payment": {
"status": "paid",
"amount_minor": 150000,
"currency": "RUB",
"paid_minor": 150000,
"refunded_minor": 0,
"paid_at": "2026-10-15T14:30:00+03:00"
}
}'{
"data": {
"id": "20000000-0000-4000-8000-000000000002",
"number": "PARK-000002",
"status": "active",
"effective_status": "valid",
"occupancy_status": "outside",
"entries_used": 0,
"entries_remaining": null,
"version": 1
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data | Ticket | Созданный билет, 201. Повтор того же ключа — 200 с исходным телом. |
Список билетов
GET /integrations/parking/ticketsПоиск билетов и выборка изменений для сверки. Сортировка по updated_at и id, граница updated_since включительна — дедуплицируйте по id и version.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
external_id | query | string | нет | Билет по ID источника. |
external_order_id | query | string | нет | Все парковочные билеты одного заказа источника. |
crm_user_id | query | integer | нет | Билеты клиента CRM. |
parking_lot_id | query | uuid | нет | Билеты одной парковки. |
event_id | query | integer | нет | Билеты, относящиеся к мероприятию CRM. |
status | query | string | нет | Управляемый статус: active, suspended, cancelled. |
effective_status | query | string | нет | Вычисляемое состояние, например expired или exhausted. |
plate_number | query | string | нет | Госномер; передаётся только вместе с country_code. |
updated_since | query | datetime | нет | Изменённые с указанного момента, граница включительна. |
limit | query | integer | нет | Размер страницы, от 1 до 100. По умолчанию — 50. |
curl "https://api.virazh.io/v1/integrations/parking/tickets?external_order_id=order-5001&limit=50" \
-H "Authorization: Bearer sk_test_51H..."{
"data": [
{
"id": "20000000-...-0001",
"number": "PARK-000001",
"external_id": "parking-ticket-10001",
"status": "active",
"effective_status": "exhausted",
"occupancy_status": "inside",
"entries_used": 1,
"entries_remaining": 0,
"updated_at": "2026-10-20T14:42:11Z",
"version": 2
}
],
"meta": { "next_cursor": null, "has_more": false }
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data | array<Ticket> | Билеты с полным составом полей карточки. |
meta.has_more | boolean | Есть ли следующая страница. |
Карточка билета
GET /integrations/parking/tickets/{ticket_id}Билет целиком: покупка, оплата, автомобиль, место, счётчики въездов и время последнего въезда и выезда.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
ticket_id | путь | uuid | да | ID билета в CRM. |
curl "https://api.virazh.io/v1/integrations/parking/tickets/20000000-0000-4000-8000-000000000001" \
-H "Authorization: Bearer sk_test_51H..."{
"data": {
"id": "20000000-0000-4000-8000-000000000001",
"number": "PARK-000001",
"status": "active",
"effective_status": "exhausted",
"occupancy_status": "inside",
"entries_used": 1,
"entries_remaining": 0,
"last_entry_at": "2026-10-20T14:42:10Z",
"last_exit_at": null,
"payment": {
"status": "paid",
"amount_minor": 150000,
"currency": "RUB",
"paid_minor": 150000,
"refunded_minor": 0
},
"vehicle": { "plate_number": "А123АА777", "country_code": "RU" },
"status_reason": null,
"version": 2
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data.occupancy_status | string | Положение автомобиля по подтверждённым проездам: outside, inside, unknown. |
data.entries_used | integer | Количество уникальных подтверждённых въездов. |
data.last_entry_at | datetime | null | Последний подтверждённый въезд. |
data.status_reason | string | null | Причина последнего изменения управляемого статуса — текст из suspend, resume или cancel. |
Компактный статус билета
GET /integrations/parking/tickets/{ticket_id}/statusКороткий ответ для интерфейса оператора и регулярной сверки: состояние, оплата, положение автомобиля и счётчики. Поле as_of — время расчёта, version — значение для If-Match.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
ticket_id | путь | uuid | да | ID билета в CRM. |
curl "https://api.virazh.io/v1/integrations/parking/tickets/20000000-0000-4000-8000-000000000001/status" \
-H "Authorization: Bearer sk_test_51H..."{
"data": {
"ticket_id": "20000000-0000-4000-8000-000000000001",
"status": "active",
"effective_status": "exhausted",
"payment_status": "paid",
"occupancy_status": "inside",
"entries_used": 1,
"entries_remaining": 0,
"last_entry_at": "2026-10-20T14:42:10Z",
"last_exit_at": null,
"as_of": "2026-10-20T14:42:11Z",
"version": 2
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data.effective_status | string | Вычисляемое право на новый въезд на момент as_of. |
data.as_of | datetime | Момент, на который посчитано состояние. |
data.version | integer | Текущая версия записи CRM для последующих изменяющих вызовов. |
Исправить билет
PATCH /integrations/parking/tickets/{ticket_id}Правка автомобиля, срока действия или комментария без повторного создания покупки. Место, парковка, оплата, source_version и счётчики здесь не меняются; нужен хотя бы один параметр.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
ticket_id | путь | uuid | да | ID билета в CRM. |
If-Match | заголовок | string | да | Текущее значение version в кавычках, например "3". Без заголовка — 428 VERSION_REQUIRED, при расхождении — 412 VERSION_CONFLICT. |
Idempotency-Key | заголовок | uuid | да | Ключ идемпотентности запроса. |
vehicle | тело | object | нет | Новый автомобиль. Смена запрещена, пока occupancy_status равен inside или unknown — сначала нужна сверка истории проездов. |
valid_from | тело | datetime | нет | Новое начало действия; период заново проверяется на пересечение брони. |
valid_until | тело | datetime | нет | Новый конец действия. |
note | тело | string | null | нет | Комментарий до 1000 символов; null очищает его. |
curl -X PATCH "https://api.virazh.io/v1/integrations/parking/tickets/20000000-0000-4000-8000-000000000001" \
-H "Authorization: Bearer sk_test_51H..." \
-H "Idempotency-Key: 40000000-0000-4000-8000-000000000012" \
-H 'If-Match: "2"' \
-H "Content-Type: application/json" \
-d '{
"vehicle": { "plate_number": "В456ВВ777", "country_code": "RU" },
"note": "Покупатель приедет на другой машине"
}'{
"data": {
"id": "20000000-0000-4000-8000-000000000001",
"vehicle": { "plate_number": "В456ВВ777", "country_code": "RU" },
"note": "Покупатель приедет на другой машине",
"status": "active",
"effective_status": "valid",
"version": 3
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data | Ticket | Билет после правки с увеличенным version. |
Заблокировать билет
POST /integrations/parking/tickets/{ticket_id}/suspendВременно запрещает новые въезды: active → suspended, повтор идемпотентен. Автомобиль внутри, история выездов и бронь места сохраняются.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
ticket_id | путь | uuid | да | ID билета в CRM. |
If-Match | заголовок | string | да | Текущее значение version в кавычках. |
Idempotency-Key | заголовок | uuid | да | Ключ идемпотентности запроса. |
reason | тело | string | да | Причина блокировки, от 1 до 1000 символов. Попадает в status_reason и в журнал. |
curl -X POST "https://api.virazh.io/v1/integrations/parking/tickets/20000000-0000-4000-8000-000000000001/suspend" \
-H "Authorization: Bearer sk_test_51H..." \
-H "Idempotency-Key: 40000000-0000-4000-8000-000000000013" \
-H 'If-Match: "3"' \
-H "Content-Type: application/json" \
-d '{ "reason": "Спор по оплате заказа" }'{
"data": {
"id": "20000000-0000-4000-8000-000000000001",
"status": "suspended",
"effective_status": "suspended",
"suspended_at": "2026-10-18T09:12:40Z",
"status_reason": "Спор по оплате заказа",
"version": 4
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data.status | string | Становится suspended. |
data.suspended_at | datetime | Время блокировки. |
Снять блокировку
POST /integrations/parking/tickets/{ticket_id}/resumeВозвращает билет в работу: suspended → active, повтор идемпотентен. Отмену, неоплату, истёкший срок и исчерпанный лимит метод не снимает — они пересчитываются в effective_status.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
ticket_id | путь | uuid | да | ID билета в CRM. |
If-Match | заголовок | string | да | Текущее значение version в кавычках. |
Idempotency-Key | заголовок | uuid | да | Ключ идемпотентности запроса. |
reason | тело | string | да | Причина снятия блокировки, от 1 до 1000 символов. |
curl -X POST "https://api.virazh.io/v1/integrations/parking/tickets/20000000-0000-4000-8000-000000000001/resume" \
-H "Authorization: Bearer sk_test_51H..." \
-H "Idempotency-Key: 40000000-0000-4000-8000-000000000014" \
-H 'If-Match: "4"' \
-H "Content-Type: application/json" \
-d '{ "reason": "Оплата подтверждена" }'{
"data": {
"id": "20000000-0000-4000-8000-000000000001",
"status": "active",
"effective_status": "valid",
"suspended_at": null,
"status_reason": "Оплата подтверждена",
"version": 5
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data.status | string | Становится active. |
data.effective_status | string | Пересчитывается заново: билет с истёкшим сроком вернётся как expired, а не valid. |
Отменить билет
POST /integrations/parking/tickets/{ticket_id}/cancelСнимает право на новые въезды, сохраняя покупку и историю: active или suspended → cancelled, восстановление не предусмотрено. Возврат денег это не выполняет: суммы возврата приходят новым снимком через PUT.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
ticket_id | путь | uuid | да | ID билета в CRM. |
If-Match | заголовок | string | да | Текущее значение version в кавычках. |
Idempotency-Key | заголовок | uuid | да | Ключ идемпотентности запроса. |
reason | тело | string | да | Причина отмены, от 1 до 1000 символов. |
curl -X POST "https://api.virazh.io/v1/integrations/parking/tickets/20000000-0000-4000-8000-000000000001/cancel" \
-H "Authorization: Bearer sk_test_51H..." \
-H "Idempotency-Key: 40000000-0000-4000-8000-000000000004" \
-H 'If-Match: "2"' \
-H "Content-Type: application/json" \
-d '{ "reason": "Покупатель отменил парковку" }'{
"data": {
"id": "20000000-0000-4000-8000-000000000001",
"status": "cancelled",
"effective_status": "cancelled",
"cancelled_at": "2026-10-19T10:04:11Z",
"status_reason": "Покупатель отменил парковку",
"version": 3
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data.status | string | Становится cancelled, обратного перехода нет. |
data.cancelled_at | datetime | Время отмены. |
Проверить доступ
POST /integrations/parking/access/checkПроверяет право на въезд или выезд прямо сейчас, не записывая проезд и не открывая шлагбаум. Отказ приходит как 200 с allowed=false и причиной в reason_code, а не как ошибка; время берётся на сервере.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
parking_lot_id | тело | uuid | да | Парковка, на которой стоит точка проезда. |
gate_id | тело | uuid | да | Точка проезда; её направление должно допускать указанный direction. |
direction | тело | string | да | entry или exit. |
ticket_id | тело | uuid | нет | Билет CRM. |
external_ticket_id | тело | string | нет | Билет по ID источника. |
access_code | тело | string | нет | Код с QR или штрихкода. Передаётся только в теле запроса — не в query-строке и не в пути. |
vehicle | тело | object | нет | Распознанный автомобиль. Нужен хотя бы один идентификатор из четырёх; если их несколько, все должны указывать на один билет, иначе 409 IDENTITY_CONFLICT. |
curl -X POST "https://api.virazh.io/v1/integrations/parking/access/check" \
-H "Authorization: Bearer sk_test_51H..." \
-H "Content-Type: application/json" \
-d '{
"parking_lot_id": "10000000-0000-4000-8000-000000000001",
"gate_id": "30000000-0000-4000-8000-000000000002",
"direction": "exit",
"ticket_id": "20000000-0000-4000-8000-000000000001"
}'{
"data": {
"allowed": true,
"reason_code": null,
"ticket_id": "20000000-0000-4000-8000-000000000001",
"effective_status": "exhausted",
"occupancy_status": "inside",
"entries_remaining": 0,
"checked_at": "2026-10-20T18:17:59Z"
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data.allowed | boolean | Разрешён ли проезд в указанном направлении прямо сейчас. |
data.reason_code | string | null | Причина отказа при allowed=false, например TICKET_EXPIRED или ALREADY_INSIDE. |
data.entries_remaining | integer | null | Сколько въездов осталось; null при безлимитном билете. |
data.checked_at | datetime | Серверное время проверки. |
Передать проезд
POST /integrations/parking/passagesЗаносит фактический въезд, выезд или отказ. Событие уникально по паре «источник + external_id»: повторная доставка вернёт 200 с duplicate=true и не расходует второй въезд, а тот же ID с другим телом — 409.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
Idempotency-Key | заголовок | uuid | да | Ключ идемпотентности запроса. |
external_id | тело | string | да | ID события у источника, одинаковый при повторной доставке одного и того же проезда. |
parking_lot_id | тело | uuid | да | Парковка, где произошло событие. |
gate_id | тело | uuid | да | Точка проезда из справочника этой парковки. |
direction | тело | string | да | entry или exit. |
result | тело | string | да | passed — подтверждённое пересечение, denied — отказ. Удачное чтение QR само по себе проездом не считается. |
reason_code | тело | string | нет | Причина отказа; обязательна при result=denied. |
occurred_at | тело | datetime | да | Фактическое время события. Больше чем на 5 минут в будущее — 422 EVENT_TIME_IN_FUTURE; прошедшие события принимаются с пересчётом истории. |
verification_method | тело | string | да | Способ идентификации: qr, barcode, plate, manual, unknown. |
ticket_id | тело | uuid | нет | Билет CRM. |
external_ticket_id | тело | string | нет | Билет по ID источника — можно передать раньше, чем поступит сам билет. |
vehicle | тело | object | нет | Распознанный автомобиль. Нужен хотя бы один из трёх: ticket_id, external_ticket_id или vehicle. |
device_id | тело | string | нет | ID контроллера или устройства. |
device_sequence | тело | integer | нет | Порядковый номер события контроллера — по нему разбирается порядок событий с одинаковым временем. |
curl -X POST "https://api.virazh.io/v1/integrations/parking/passages" \
-H "Authorization: Bearer sk_test_51H..." \
-H "Idempotency-Key: 40000000-0000-4000-8000-000000000002" \
-H "Content-Type: application/json" \
-d '{
"external_id": "gate-event-7001",
"parking_lot_id": "10000000-0000-4000-8000-000000000001",
"gate_id": "30000000-0000-4000-8000-000000000001",
"external_ticket_id": "parking-ticket-10001",
"vehicle": { "plate_number": "А123АА777", "country_code": "RU" },
"direction": "entry",
"result": "passed",
"occurred_at": "2026-10-20T17:42:10+03:00",
"device_id": "gate-controller-1",
"device_sequence": 7001,
"verification_method": "plate"
}'{
"data": {
"id": "50000000-0000-4000-8000-000000000001",
"external_id": "gate-event-7001",
"ticket_id": "20000000-0000-4000-8000-000000000001",
"link_status": "linked",
"reconciliation_status": "applied",
"direction": "entry",
"result": "passed",
"occurred_at": "2026-10-20T14:42:10Z",
"received_at": "2026-10-20T14:42:11Z",
"duplicate": false
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data.link_status | string | Результат связывания с билетом: linked, unmatched — билет пока не поступил, ambiguous — подходит несколько. |
data.reconciliation_status | string | Состояние пересчёта истории: applied, pending, needs_review при аномалии в последовательности. |
data.duplicate | boolean | true, если событие с таким external_id уже было получено ранее. |
data.received_at | datetime | Когда событие приняла платформа; occurred_at оно не заменяет. |
Передать пачку проездов
POST /integrations/parking/passages/batchДо 500 событий за раз, включая накопленные без связи. Каждый элемент идёт в своей транзакции по порядку: ошибка одного не откатывает остальные, исправленные элементы досылаются новой пачкой с новым ключом.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
Idempotency-Key | заголовок | uuid | да | Ключ идемпотентности пачки. |
items | тело | array<passage_input> | да | От 1 до 500 событий того же состава, что и в одиночном методе. Размер тела — до 2 МБ, иначе 413 PAYLOAD_TOO_LARGE. |
curl -X POST "https://api.virazh.io/v1/integrations/parking/passages/batch" \
-H "Authorization: Bearer sk_test_51H..." \
-H "Idempotency-Key: 40000000-0000-4000-8000-000000000021" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"external_id": "gate-event-7002",
"parking_lot_id": "10000000-0000-4000-8000-000000000001",
"gate_id": "30000000-0000-4000-8000-000000000002",
"external_ticket_id": "parking-ticket-10001",
"direction": "exit",
"result": "passed",
"occurred_at": "2026-10-20T21:18:00+03:00",
"verification_method": "plate"
}
]
}'{
"data": {
"received": 2,
"created": 1,
"duplicates": 1,
"errors": 0,
"results": [
{
"index": 0,
"external_id": "gate-event-7002",
"http_status": 201,
"data": { "id": "50000000-...-0002", "link_status": "linked" },
"error": null
},
{
"index": 1,
"external_id": "gate-event-7001",
"http_status": 200,
"data": { "id": "50000000-...-0001", "duplicate": true },
"error": null
}
]
}
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data.received | integer | Сколько элементов пришло: received = created + duplicates + errors. |
data.created | integer | Сколько событий сохранено впервые. |
data.duplicates | integer | Сколько событий уже было получено ранее. |
data.results | array<Result> | Ровно один элемент на каждый элемент запроса, с сохранением его index: http_status, data и error. |
История проездов
GET /integrations/parking/passagesВъезды, выезды и отказы, включая не связанные с билетом. Сортировка по received_at и id: опоздавшие события догружаются через received_since, а не occurred_from.
Параметры
| Параметр | Где | Тип | Обязателен | Описание |
|---|---|---|---|---|
ticket_id | query | uuid | нет | Проезды одного билета. |
parking_lot_id | query | uuid | нет | Проезды одной парковки. |
gate_id | query | uuid | нет | Проезды одной точки. |
direction | query | string | нет | entry или exit. |
result | query | string | нет | passed или denied. |
link_status | query | string | нет | linked, unmatched или ambiguous — так находят события, требующие разбора. |
occurred_from | query | datetime | нет | Нижняя граница времени события, включительно. |
occurred_to | query | datetime | нет | Верхняя граница времени события, исключительно. |
received_since | query | datetime | нет | События, принятые платформой с указанного момента — включая те, что произошли раньше и пришли с опозданием. |
limit | query | integer | нет | Размер страницы, от 1 до 100. По умолчанию — 50. |
curl "https://api.virazh.io/v1/integrations/parking/passages?link_status=unmatched&limit=50" \
-H "Authorization: Bearer sk_test_51H..."{
"data": [
{
"id": "50000000-...-0003",
"external_id": "gate-event-7003",
"ticket_id": null,
"link_status": "unmatched",
"reconciliation_status": "pending",
"direction": "entry",
"result": "passed",
"vehicle": { "plate_number": "С789СС777", "country_code": "RU" },
"occurred_at": "2026-10-20T15:02:40Z",
"received_at": "2026-10-20T15:03:01Z",
"duplicate": false
}
],
"meta": { "next_cursor": null, "has_more": false }
}Поля ответа
| Поле | Тип | Описание |
|---|---|---|
data | array<Passage> | События с направлением, результатом, точкой, автомобилем и билетом. |
meta.next_cursor | string | null | Курсор следующей страницы. |
Состояние билета
status — управляемое состояние, его меняют только suspend, resume и cancel. effective_status — право на новый въезд: считается сервером, первое выполнившееся условие и даёт результат.
| Состояние | Приоритет | Условие |
|---|---|---|
cancelled | 1 | status = cancelled |
refunded | 2 | payment.status = refunded |
suspended | 3 | status = suspended |
not_paid | 4 | payment.status не paid и не partially_refunded |
not_started | 5 | now < valid_from |
expired | 6 | now ≥ valid_until |
exhausted | 7 | max_entries задан и entries_used ≥ max_entries |
valid | 8 | Ни одно условие выше не выполняется |
Въезд и выезд
| Ситуация | Решение |
|---|---|
Въезд | Разрешён при effective_status = valid и occupancy_status = outside |
Въезд, автомобиль внутри | Отказ ALREADY_INSIDE |
Выезд, автомобиль внутри | Разрешён всегда: срок, лимит, оплата и отмена не мешают |
Выезд, outside или unknown | Автоматический отказ, нужен оператор |
max_entries = 1 | Один въезд и последующий выезд |
max_entries > 1 или null | Въезды повторяются после выездов, внутри один автомобиль |
Точка проезда | Должна относиться к той же парковке и допускать направление |
Нумерованное место | Пересечение периодов двух оплаченных билетов запрещено |
Зона без номеров | Проверяется вместимость на весь период |
Блокировка билета | Бронь места не освобождает |
Отмена и полный возврат | Освобождают будущую бронь, но не занятое место |
Сверка проездов
| Событие | Что делает платформа |
|---|---|
passed + entry | Расходует въезд, переводит в inside |
passed + exit | Переводит в outside |
denied | Не расходует въезд и не меняет занятость |
Повторная доставка | 200, duplicate = true, счётчики не меняются |
Тот же external_id, другое тело | 409: история не переписывается |
Проезд раньше билета | link_status = unmatched, связывается после импорта билета |
Опоздавшее событие | Принимается, идёт пересчёт: pending и STATE_RECONCILING в проверке |
Невозможная последовательность | occupancy_status = unknown, needs_review; проезд не достраивается |
Только госномер | Связывается при единственном кандидате, иначе unmatched или ambiguous |
Разные ticket_id и external_ticket_id | 409 IDENTITY_CONFLICT |
occurred_at в будущем | Больше 5 минут — 422 EVENT_TIME_IN_FUTURE |
Коды причин
Приходят в reason_code: в ответе /access/check при allowed: false и в проезде с result: denied, где поле обязательно.
| Код | Направление | Что означает |
|---|---|---|
TICKET_NOT_FOUND | въезд и выезд | Билет не найден |
TICKET_NOT_PAID | въезд | Билет не оплачен |
TICKET_NOT_STARTED | въезд | Период действия ещё не начался |
TICKET_EXPIRED | въезд | Период действия закончился |
TICKET_CANCELLED | въезд | Билет отменён |
TICKET_SUSPENDED | въезд | Билет заблокирован |
TICKET_REFUNDED | въезд | Оплата возвращена полностью |
ENTRY_LIMIT_REACHED | въезд | Лимит въездов исчерпан |
ALREADY_INSIDE | въезд | Автомобиль по этому билету уже внутри |
NOT_INSIDE | выезд | Нет подтверждения, что автомобиль внутри |
UNKNOWN_OCCUPANCY | въезд и выезд | Неоднозначная история проездов |
STATE_RECONCILING | въезд и выезд | История пересчитывается |
PARKING_CLOSED | въезд | Парковка закрыта для въезда |
SPACE_UNAVAILABLE | въезд | Место недоступно |
WRONG_PARKING | въезд и выезд | Билет на другую парковку |
WRONG_GATE_DIRECTION | въезд и выезд | Точка не допускает это направление |
PLATE_MISMATCH | въезд | Автомобиль не совпадает с билетом |
AMBIGUOUS_TICKET | въезд и выезд | Подходит несколько билетов |
Ошибки раздела
Тело ошибки — { "error": { "code", "message", "details", "request_id" } }. Логируйте request_id: с ним разбор обращения занимает минуты.
| Статус | Код | Когда возникает |
|---|---|---|
409 | EXTERNAL_ID_CONFLICT | Билет с таким external_id уже есть, нужен PUT |
409 | STALE_SOURCE_VERSION | Снимок старее сохранённого |
409 | SOURCE_VERSION_CONFLICT | Та же source_version, другой состав полей |
409 | SPACE_CONFLICT | Место занято другой покупкой в этом периоде |
409 | CAPACITY_EXCEEDED | Исчерпана вместимость зоны или парковки |
409 | IDEMPOTENCY_CONFLICT | Тот же Idempotency-Key с другим телом |
409 | IDENTITY_CONFLICT | Идентификаторы указывают на разные билеты |
409 | EVENT_CONFLICT | Событие с таким external_id принято с другим телом |
409 | INVALID_STATE | Переход статуса недопустим |
412 | VERSION_CONFLICT | If-Match не совпал, перечитайте билет |
413 | PAYLOAD_TOO_LARGE | Пачка проездов больше 2 МБ |
422 | EVENT_TIME_IN_FUTURE | occurred_at дальше 5 минут в будущем |
422 | INVALID_BATCH | Пачка пустая, больше 500 элементов или неверной структуры |
428 | VERSION_REQUIRED | Не передан If-Match |
Что появляется в CRM
| Раздел CRM | Что появляется |
|---|---|
Клиент | Связь по crm_user_id или customer.external_id; профиль создаётся при достаточных контактах |
Заказ | Позиция типа «парковка»; повторный импорт обновляет её, сумма учитывается один раз |
Билет | Отдельная сущность со ссылками на заказ, клиента и мероприятие |
Проходы | Тип «парковка» с фильтром по направлению; denied показан отказом |
Аналитика | Выручка парковки отдельной категорией; проезды не идут в посещаемость арены |
Людей не объединяют автоматически по госномеру или совпадению имени, согласия на рассылки парковка не меняет.
Полезно знать
- Основной метод интеграции —
PUT /tickets/external/{external_id}: при повторной выгрузке заказа он обновит запись, аPOST /ticketsвернёт 409. - Три купленных места — это три билета с разными
external_idиexternal_order_line_id, а не один билет с количеством. - Проезд передаётся по факту пересечения: успешное чтение QR-кода проездом не считается.
- Для сверки берите
received_sinceу проездов иupdated_sinceу билетов — иначе опоздавшие события пройдут мимо выборки. effective_status: exhaustedприoccupancy_status: inside— не ошибка, а обычное состояние разового билета с машиной на парковке.
