# Билеты

Ссылки на билет для гостя, поиск, отмена, перевыпуск QR, повторное письмо и выгрузка билетов события в CSV.

Один билет — один гость и один QR. Билеты выпускает [заказ](https://budu.events/docs/orders/), [ручная выдача или список гостей](https://budu.events/docs/issue/).

## Объект билета {#ticket-object}

| Поле | Тип | Описание |
|---|---|---|
| `id` | string | UUID билета |
| `org_id`, `livemode` | string, boolean | Организация и режим |
| `order_id`, `order_item_id` | string | Заказ и позиция заказа |
| `event_id`, `tariff_id` | string | Событие и тариф |
| `holder_first_name`, `holder_last_name`, `holder_email` | string | Владелец билета |
| `status` | string | `valid` (действует) или `cancelled` (отменён) |
| `qr_version` | integer | Версия QR, растёт с каждым перевыпуском |
| `public_token` | string | Секрет ссылки на билет: `https://budu.events/t/<public_token>` |
| `qr_payload` | string | Что зашито в QR: строка `BT1:…` с подписью |
| `seat` | object | Место `{id, sector, row, number, label}` или `null` |
| `created_at_ms` | string | Когда выпущен, миллисекунды Unix |
| `number` | string | Номер на билете: последние 8 знаков `id` заглавными, например `1E4F9F0A`. По нему гость и контролёр находят билет |
| `checked_in_at_ms` | string | Когда гость прошёл. `"0"`: не проходил |
| `entrance` | string | Вход, на котором прошёл гость, то есть имя устройства контролёра. Пусто: не проходил |

> `public_token` и `qr_payload` пускают на событие. Не выводите их в логи, не отправляйте третьим лицам и показывайте только владельцу билета.

## Ссылки для гостя {#links}

Ссылки работают без входа и регистрации. Мы присылаем их гостю в письме, а вы можете показать их у себя на сайте сразу после оплаты.

| Адрес | Что там |
|---|---|
| `https://budu.events/t/<token>` | Страница билета: QR, событие, место, маршрут, «В календарь» |
| `https://budu.events/t/<token>/qr.svg`, `/qr.png` | QR-код картинкой |
| `https://budu.events/t/<token>/ticket.png` | Билет одной картинкой, чтобы сохранить в галерею |
| `https://budu.events/t/<token>/event.ics` | Событие для календаря |
| `https://budu.events/o/<token>` | Все билеты заказа на одной странице. Эта ссылка есть в письме, если билетов в заказе несколько |

`<token>` — это `public_token` билета. Ссылка на билет из заказа уже собрана: `tickets[].url`. После [перевыпуска](#reissue) старые ссылки отвечают `410` «Билет перевыпущен».

## Список билетов {#list}

`GET /v1/tickets`

`GET /v1/events/{id}/tickets`

Билеты организации в режиме ключа или билеты одного события, новые сверху. Для каждого билета видно, прошёл ли гость.

| Параметр | Описание |
|---|---|
| `event_id` | Только билеты этого события (для `GET /v1/tickets`) |
| `q` | Поиск: имя и фамилия в любом порядке, почта, номер билета или подпись места. От 2 до 100 символов |
| `status` | `valid` или `cancelled` |
| `limit` | Билетов на странице, от 1 до 200, по умолчанию 50 |
| `after` | Значение `next` из предыдущего ответа |

```bash Запрос
curl "https://api.budu.events/v1/events/0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60/tickets?q=Соколова&limit=20" \
  -H "Authorization: Bearer $BUDU_KEY"
```

```json Ответ 200
{
  "tickets": [
    {
      "id": "0193e0c5-1f6b-7e02-a3c9-5b8d1e4f9f0a",
      "org_id": "0193d9a1-2b3c-7d4e-8f50-61728394a5b6",
      "livemode": false,
      "order_id": "0193e0c5-1f20-7c3e-b6a1-9d4e2f7a8b10",
      "order_item_id": "0193e0c5-1f21-7a00-b1c2-d3e4f5a6b7c8",
      "event_id": "0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60",
      "tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c71",
      "holder_first_name": "Мария",
      "holder_last_name": "Соколова",
      "holder_email": "maria@example.ru",
      "status": "valid",
      "qr_version": 1,
      "public_token": "Vw3kqP7nX2bLmR9tYc4sHa",
      "qr_payload": "BT1:…",
      "seat": null,
      "created_at_ms": "1792670404000",
      "number": "1E4F9F0A",
      "checked_in_at_ms": "0",
      "entrance": ""
    }
  ],
  "next": ""
}
```

`next` пустой на последней странице.

## Получить билет {#get}

`GET /v1/tickets/{id}`

```bash Запрос
curl https://api.budu.events/v1/tickets/0193e0c5-1f6b-7e02-a3c9-5b8d1e4f9f0a \
  -H "Authorization: Bearer $BUDU_KEY"
```

Ответ `200` с [билетом](#ticket-object). Здесь `checked_in_at_ms` и `entrance` всегда пустые: проход виден в [списке билетов](#list) и в [отчёте о проходах](https://budu.events/docs/devices/#checkin).

## Отменить билет {#cancel}

`POST /v1/tickets/{id}/cancel`

QR перестаёт пускать на вход, гостю уходит письмо об отмене. Деньги возвращаете вы.

| Поле | Тип | Описание |
|---|---|---|
| `return_to_sale` | boolean | `true` (по умолчанию): билет или место снова в продаже. `false`: остаток не меняется |

```bash Запрос
curl https://api.budu.events/v1/tickets/0193e0c5-1f6b-7e02-a3c9-5b8d1e4f9f0a/cancel \
  -H "Authorization: Bearer $BUDU_KEY" \
  -H "Idempotency-Key: refund-1042-1" \
  -H "Content-Type: application/json" \
  -d '{"return_to_sale": true}'
```

```json Ответ 200
{
  "ticket": {
    "id": "0193e0c5-1f6b-7e02-a3c9-5b8d1e4f9f0a",
    "url": "https://budu.events/t/Vw3kqP7nX2bLmR9tYc4sHa",
    "tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c71",
    "holder_first_name": "Мария",
    "holder_last_name": "Соколова",
    "status": "cancelled",
    "seat": null
  }
}
```

Тело можно не передавать. Неизвестное поле, например опечатка `retrun_to_sale`, даёт `400`: так возврат в продажу не включится по ошибке. Повторная отмена отвечает так же.

## Перевыпустить {#reissue}

`POST /v1/tickets/{id}/reissue`

Новый QR и новая ссылка, если билет переслали не тому или QR попал в чужие руки. Старый QR на входе больше не пускает, старая ссылка отвечает «Билет перевыпущен». Гостю уходит письмо с новым билетом.

```bash Запрос
curl -X POST https://api.budu.events/v1/tickets/0193e0c5-1f6b-7e02-a3c9-5b8d1e4f9f0a/reissue \
  -H "Authorization: Bearer $BUDU_KEY"
```

Ответ `200` с [билетом](#ticket-object): новые `public_token` и `qr_payload`, `qr_version` больше на 1. Ошибки: `400` «Отменённый билет нельзя перевыпустить», `429` при перевыпуске чаще раза в минуту.

## Отправить письмо ещё раз {#resend}

`POST /v1/tickets/{id}/resend`

Письмо с билетом уходит владельцу ещё раз, на тот же адрес.

```bash Запрос
curl -X POST https://api.budu.events/v1/tickets/0193e0c5-1f6b-7e02-a3c9-5b8d1e4f9f0a/resend \
  -H "Authorization: Bearer $BUDU_KEY"
```

```json Ответ 202
{"status": "queued"}
```

Не чаще раза в минуту на билет, иначе `429 rate_limited`. В тестовом режиме письмо уходит, только если адрес среди [тестовых адресатов](https://budu.events/docs/test-mode/#recipients).

## Выгрузить в CSV {#export}

`GET /v1/events/{id}/tickets.csv`

Все билеты события одним файлом для Excel: UTF-8 с BOM, разделитель `;`, время в часовом поясе события.

```bash Запрос
curl -o tickets.csv https://api.budu.events/v1/events/0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60/tickets.csv \
  -H "Authorization: Bearer $BUDU_KEY"
```

```csv
номер;имя;фамилия;почта;тариф;место;статус;выдан;прошёл;вход
1E4F9F0A;Мария;Соколова;maria@example.ru;Танцпол;;действует;2026-10-22 15:00;2026-12-12 18:42;Вход 1
```

Значение, которое начинается с `=`, `+`, `-` или `@`, получает впереди апостроф, чтобы Excel не принял его за формулу.
