# Events

An event is a date, a venue and tiers. Create it as a draft, publish it, and sales open.

A new event starts as a draft (`draft`). You can change a draft any way you like, but you can't sell tickets for it. Publishing (`published`) opens sales, and after that the tiers no longer change.

## Event object {#event-object}

| Field | Type | Description |
|---|---|---|
| `id` | string | Event UUID |
| `org_id` | string | Organization UUID |
| `livemode` | boolean | `true` for a live event, `false` for a test one |
| `title` | string | Title, up to 200 characters |
| `starts_at`, `ends_at` | string | Start and end, RFC 3339 in UTC |
| `timezone` | string | IANA time zone, such as `Europe/Moscow`. The email and the ticket show times in it |
| `venue_name`, `venue_address` | string | Venue and address, up to 300 characters |
| `status` | string | `draft` (draft) or `published` (on sale) |
| `tariffs` | array | Tiers, see below |
| `has_seats` | boolean | The event has a tier with seats |
| `ticket_style` | string | Ticket look: `ticket` (a white ticket with a tear-off line) or `pass` (a pass with a lime header) |
| `cover` | string | Cover version. An empty string means no cover |

### Tier {#tariff-object}

| Field | Type | Description |
|---|---|---|
| `id` | string | Tier UUID. You pass it in an order |
| `code` | string | Code: Latin letters, digits, `_` and `-`, up to 32 characters. Unique within the event. Defaults to `t1`, `t2`… |
| `name` | string | Name, up to 100 characters. Never repeats within the event, case-insensitive |
| `price_minor` | string | Price in kopecks. A number or a string in requests, a string in responses |
| `currency` | string | Three-letter currency code, `RUB` by default |
| `quota` | integer | How many tickets to sell, 1 to 1,000,000. For a tier with seats it equals the number of seats |
| `per_order_limit` | integer | How many tickets of the tier fit in one order. `0` means no limit |
| `seated` | boolean | A tier with seats, see [assigned seating](https://budu.events/en/docs/seats/) |
| `seats` | integer | Response only: how many seats the tier has |

## Create an event {#create}

`POST /v1/events`

| Field | Type | Required | Description |
|---|---|---|---|
| `title` | string | yes | Title |
| `starts_at` | string | yes | Start, RFC 3339 |
| `ends_at` | string | yes | End, later than the start |
| `timezone` | string | yes | IANA time zone |
| `venue_name` | string | no | Venue |
| `venue_address` | string | no | Address, the guest gets directions to it |
| `ticket_style` | string | no | `ticket` (default) or `pass` |
| `tariffs` | array | no | Tiers: `code`, `name`, `price_minor`, `currency`, `quota`, `per_order_limit`, `seated`. A draft doesn't need tiers, publishing needs at least one |

```bash Request
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 Response 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": ""
}
```

Errors: `400 invalid_request` with the reason in `message`. For example: no title, the end before the start, an unknown time zone, a repeated tier code or name, a quota out of range. If a tier is at fault, its code is in `details.code`.

## List events {#list}

`GET /v1/events`

Every event of the organization in the key's mode, by start time.

```bash Request
curl https://api.budu.events/v1/events -H "Authorization: Bearer $BUDU_KEY"
```

You get `200` with `{"events": [...]}`, each event an [event object](#event-object) with every field.

## Get an event {#get}

`GET /v1/events/{id}`

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

You get `200` with the [event object](#event-object). Another organization's event or an event of the other mode gives `404 not_found`.

## Update an event {#update}

`PATCH /v1/events/{id}`

Only the fields you send change, the rest stay as they were. You can send `title`, `starts_at`, `ends_at`, `timezone`, `venue_name`, `venue_address`, `ticket_style` and `tariffs`. Any other field gives `400 invalid_request` with the field name in `details.field`.

- `tariffs` replaces the draft's tiers as a whole. To keep a tier, pass its `id`. A tier without an `id` is new, and a tier missing from the list is deleted together with its seats.
- A published event's tiers don't change: `400 invalid_request`. You can still change the title, times, venue and ticket look.

```bash Request
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"}'
```

You get `200` with the updated event.

## Publish {#publish}

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

Opens sales. You need at least one tier, and tiers with seats need their seats uploaded.

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

You get `200` with the event in `published` status. Stock reaches sales within a few seconds. Until then an order gets `409 event_not_on_sale`. Publishing again breaks nothing and answers the same.

Errors: `400 invalid_request`: «Добавьте хотя бы один тариф» (add at least one tier), «Загрузите места для тарифа «…»» (upload the seats for the tier, `details.tariff_id`).

## Availability {#availability}

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

How many tickets are left in each tier. Handy for showing "12 left" on your site or closing sales of a tier.

| Parameter | Description |
|---|---|
| `seats` | `1`: add seat states |
| `seats_after` | `id` of the last seat on the previous page |
| `seats_limit` | Seats per page, 1 to 5,000 |
| `sector` | Only the seats of this sector |

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

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

`held` counts tickets in active reservations: nobody has paid for them yet. `available` = `quota` − `sold` − `held`. Seat states are described in [assigned seating](https://budu.events/en/docs/seats/#states).

## Cover {#cover}

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

The event's picture. The guest sees it as a band across the top of the ticket, on the page and in the email. The request body is the picture itself, not a form.

```bash Request
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 or WebP. We check the format by the file's content, not by the header.
- Up to 5 MB and 25 megapixels, at least 640 px wide, landscape: 3:2 to 2:1. 16:9 works best, such as 1920×1080.
- The ticket shows the cover at 16:9: a 3:2 picture loses its top and bottom edges. Keep text and faces near the center.
- We store our own copy: a JPEG up to 1,200 px wide with no metadata. A transparent PNG background turns white.
- You can change the cover in any event status. The ticket page shows the new one right away, emails already sent keep the old one.

You get `200` with the event, with the new version in `cover`. Errors: `400 invalid_request` with the reason in `message`. If the proportions or the size don't fit, `details` holds `width` and `height`.

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

Removes the cover. You get `200` with the event, `cover` empty.
