# События

Событие — это дата, площадка и тарифы. Создайте его черновиком, опубликуйте, и продажа откроется.

Новое событие создаётся черновиком (`draft`). Черновик можно менять как угодно, но продавать билеты на него нельзя. После публикации (`published`) открывается продажа, а тарифы больше не меняются.

## Объект события {#event-object}

| Поле | Тип | Описание |
|---|---|---|
| `id` | string | UUID события |
| `org_id` | string | UUID организации |
| `livemode` | boolean | `true` для боевого события, `false` для тестового |
| `title` | string | Название, до 200 символов |
| `starts_at`, `ends_at` | string | Начало и окончание, RFC 3339 в UTC |
| `timezone` | string | Часовой пояс IANA, например `Europe/Moscow`. В нём время показывается в письме и на билете |
| `venue_name`, `venue_address` | string | Площадка и адрес, до 300 символов |
| `status` | string | `draft` (черновик) или `published` (в продаже) |
| `tariffs` | array | Тарифы, см. ниже |
| `has_seats` | boolean | У события есть тариф с местами |
| `ticket_style` | string | Вид билета: `ticket` (белый билет с линией отрыва) или `pass` (пропуск с лаймовой шапкой) |
| `cover` | string | Версия обложки. Пустая строка: обложки нет |

### Тариф {#tariff-object}

| Поле | Тип | Описание |
|---|---|---|
| `id` | string | UUID тарифа. Его передают в заказе |
| `code` | string | Код: латиница, цифры, `_` и `-`, до 32 символов. Уникален в событии. По умолчанию `t1`, `t2`… |
| `name` | string | Название, до 100 символов. Не повторяется в событии без учёта регистра |
| `price_minor` | string | Цена в копейках. В запросе число или строка, в ответе строка |
| `currency` | string | Трёхбуквенный код валюты, по умолчанию `RUB` |
| `quota` | integer | Сколько билетов продать, от 1 до 1 000 000. У тарифа с местами равна числу мест |
| `per_order_limit` | integer | Сколько билетов тарифа можно взять в один заказ. `0`: без лимита |
| `seated` | boolean | Тариф с местами, см. [места в зале](https://budu.events/docs/seats/) |
| `seats` | integer | Только в ответе: сколько мест у тарифа |

## Создать событие {#create}

`POST /v1/events`

| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
| `title` | string | да | Название |
| `starts_at` | string | да | Начало, RFC 3339 |
| `ends_at` | string | да | Окончание, позже начала |
| `timezone` | string | да | Часовой пояс IANA |
| `venue_name` | string | нет | Площадка |
| `venue_address` | string | нет | Адрес, по нему гость построит маршрут |
| `ticket_style` | string | нет | `ticket` (по умолчанию) или `pass` |
| `tariffs` | array | нет | Тарифы: `code`, `name`, `price_minor`, `currency`, `quota`, `per_order_limit`, `seated`. Черновику тарифы не обязательны, для публикации нужен хотя бы один |

```bash Запрос
curl https://api.budu.events/v1/events \
  -H "Authorization: Bearer $BUDU_KEY" \
  -H "Idempotency-Key: event-jazz-2026" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Джаз в саду",
    "starts_at": "2026-12-12T16:00:00Z",
    "ends_at": "2026-12-12T19:00:00Z",
    "timezone": "Europe/Moscow",
    "venue_name": "Площадка «Сад»",
    "venue_address": "Москва, Садовая улица, 1",
    "ticket_style": "ticket",
    "tariffs": [
      {
        "code": "dance",
        "name": "Танцпол",
        "price_minor": 250000,
        "quota": 300,
        "per_order_limit": 6
      }
    ]
  }'
```

```json Ответ 201
{
  "id": "0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60",
  "org_id": "0193d9a1-2b3c-7d4e-8f50-61728394a5b6",
  "livemode": false,
  "title": "Джаз в саду",
  "starts_at": "2026-12-12T16:00:00Z",
  "ends_at": "2026-12-12T19:00:00Z",
  "timezone": "Europe/Moscow",
  "venue_name": "Площадка «Сад»",
  "venue_address": "Москва, Садовая улица, 1",
  "status": "draft",
  "tariffs": [
    {
      "id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c71",
      "code": "dance",
      "name": "Танцпол",
      "price_minor": "250000",
      "currency": "RUB",
      "quota": 300,
      "per_order_limit": 6,
      "seated": false,
      "seats": 0
    }
  ],
  "has_seats": false,
  "ticket_style": "ticket",
  "cover": ""
}
```

Ошибки: `400 invalid_request` с причиной в `message`. Например, нет названия, окончание раньше начала, неизвестный часовой пояс, повторяется код или название тарифа, квота вне диапазона. Если ошибка в тарифе, его код будет в `details.code`.

## Список событий {#list}

`GET /v1/events`

Все события организации в режиме ключа, по времени начала.

```bash Запрос
curl https://api.budu.events/v1/events -H "Authorization: Bearer $BUDU_KEY"
```

Ответ `200`: `{"events": [...]}`, каждое событие — [объект события](#event-object) со всеми полями.

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

`GET /v1/events/{id}`

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

Ответ `200` с [объектом события](#event-object). Чужое событие или событие другого режима: `404 not_found`.

## Изменить событие {#update}

`PATCH /v1/events/{id}`

Меняются только переданные поля, остальные остаются как были. Можно передать `title`, `starts_at`, `ends_at`, `timezone`, `venue_name`, `venue_address`, `ticket_style` и `tariffs`. Любое другое поле даёт `400 invalid_request` с именем поля в `details.field`.

- `tariffs` целиком заменяет тарифы черновика. Чтобы оставить тариф, передайте его `id`. Тариф без `id` будет новым, а тариф, которого нет в списке, удалится вместе со своими местами.
- У опубликованного события тарифы не меняются: `400 invalid_request`. Название, время, площадку и вид билета менять можно.

```bash Запрос
curl -X PATCH https://api.budu.events/v1/events/0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60 \
  -H "Authorization: Bearer $BUDU_KEY" \
  -H "Content-Type: application/json" \
  -d '{"venue_name": "Летняя сцена", "ticket_style": "pass"}'
```

Ответ `200` с событием после изменения.

## Опубликовать {#publish}

`POST /v1/events/{id}/publish`

Открывает продажу. Нужен хотя бы один тариф, а у тарифов с местами должны быть загружены места.

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

Ответ `200` с событием в статусе `published`. Остатки доходят до продажи за несколько секунд. Пока этого не случилось, заказ получит `409 event_not_on_sale`. Повторная публикация ничего не ломает и отвечает тем же.

Ошибки: `400 invalid_request`: «Добавьте хотя бы один тариф», «Загрузите места для тарифа «…»» (`details.tariff_id`).

## Остатки {#availability}

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

Сколько билетов осталось по каждому тарифу. Удобно, чтобы показать на сайте «осталось 12» или закрыть продажу тарифа.

| Параметр | Описание |
|---|---|
| `seats` | `1`: добавить состояния мест |
| `seats_after` | `id` последнего места предыдущей страницы |
| `seats_limit` | Мест на странице, от 1 до 5 000 |
| `sector` | Только места этого сектора |

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

```json Ответ 200
{
  "tariffs": [
    {
      "tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c71",
      "quota": 300,
      "sold": 212,
      "available": 79,
      "held": 9,
      "seated": false
    }
  ],
  "seats": [],
  "seats_last": false
}
```

`held` — билеты в действующих резервах: их ещё не оплатили. `available` = `quota` − `sold` − `held`. Состояния мест описаны в разделе [места в зале](https://budu.events/docs/seats/#states).

## Обложка {#cover}

`PUT /v1/events/{id}/cover`

Картинка события. Гость видит её полосой сверху билета, на странице и в письме. Тело запроса — сама картинка, а не форма.

```bash Запрос
curl -X PUT https://api.budu.events/v1/events/0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60/cover \
  -H "Authorization: Bearer $BUDU_KEY" \
  -H "Content-Type: image/jpeg" \
  --data-binary @cover.jpg
```

- Формат JPEG, PNG или WebP. Его проверяем по содержимому файла, а не по заголовку.
- До 5 МБ и 25 мегапикселей, ширина от 640 px, горизонтальная: от 3:2 до 2:1. Лучше всего 16:9, например 1920×1080.
- На билете обложка показывается в пропорции 16:9: у картинки 3:2 срежутся края сверху и снизу. Надписи и лица держите ближе к центру.
- Мы храним свою копию: JPEG шириной до 1 200 px без метаданных. Прозрачный фон PNG станет белым.
- Обложку можно менять в любом статусе события. Страница билета сразу покажет новую, отправленные письма останутся со старой.

Ответ `200` с событием, в `cover` новая версия. Ошибки: `400 invalid_request` с причиной в `message`. Если не подошла пропорция или размер, в `details` будут `width` и `height`.

`DELETE /v1/events/{id}/cover`

Убирает обложку. Ответ `200` с событием, `cover` пустой.
