RU
Docs sections

Assigned seating

Tickets for specific seats: a tier with seats, the seat map as a CSV file, and ordering a seat by its label or your own ID.

A seat always belongs to one tier, that is, one price category. One event can mix tiers with seats and tiers with a quota: for example, stalls by seat and a dance floor without seats. A seat sells once, even when two people buy it in the same second.

Set up an event with seats

  1. Create an event with a "seated": true tier. Don't pass a quota for such a tier: it equals the number of its seats.
  2. Upload the seats as a CSV file: POST /v1/events/{id}/seats. You can check the file first with ?dry_run=1.
  3. Publish the event. A tier with seats can't be published without its seats.

Seats change only on a draft. An upload replaces every seat of the event.

CSV format

csv

sector,row,number,label,external_id,tariff
Партер,1,1,П-1-1,hall-0001,parter
Партер,1,2,,hall-0002,parter
"Балкон, левая сторона",1,1,Б-1-1,hall-0101,balcony
ColumnRequiredDescription
sectoryesSector, up to 50 characters
rowyesRow, up to 20 characters
numberyesSeat number, up to 20 characters
labelnoSeat label, unique within the event, up to 100 characters. Empty means sector-row-number, in the example's second line Партер-1-2
external_idnoYour own seat ID, for example from the hall plan. Unique within the event and never equal to any label
tariffif there are several tiers with seatsCode or name of the tier with seats
  • The first line is the header. Case and spaces around names don't matter, columns go in any order. Any other column is an error.
  • The separator is , or ; (Excel with Russian settings saves with ;). RFC 4180 quotes, a BOM and Windows line breaks are fine.
  • UTF-8 only. In Excel, save as "CSV UTF-8".
  • Empty lines are skipped. Control characters inside values are not allowed.
  • Line order is the order of seats in the hall: lists and availability return seats in it.
  • Up to 50,000 seats and 5 MB per file.

Upload seats

POST/v1/events/{id}/seats

The request body is the file itself, with Content-Type: text/csv. The file is accepted whole or not at all. ?dry_run=1 checks and counts the seats without writing anything.

Request

curl "https://api.budu.events/v1/events/0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60/seats?dry_run=1" \
  -H "Authorization: Bearer $BUDU_KEY" \
  -H "Content-Type: text/csv" \
  --data-binary @hall.csv

Response 200

{
  "total": 3,
  "per_tariff": [
    {"tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c73", "count": 2},
    {"tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c74", "count": 1}
  ],
  "errors": [],
  "sample": [
    {
      "id": "0193e0b3-0a11-7f00-8d2b-41c6e9a7b001",
      "tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c73",
      "sector": "Партер",
      "row": "1",
      "number": "1",
      "label": "П-1-1",
      "external_id": "hall-0001",
      "position": 1
    }
  ]
}

sample holds the first 20 seats for a preview. If the file has errors, the response is 400 invalid_request, nothing is written, and the errors are in details.errors:

Response 400

{
  "error": {
    "code": "invalid_request",
    "message": "В файле мест есть ошибки — места не загружены",
    "details": {
      "errors": [
        {"line": 3, "message": "Место «П-1-1» повторяется: строки 2 и 3"}
      ]
    }
  }
}

line is the file's line number, with the header as line 1. line: 0 refers to the file as a whole. The response holds up to 100 errors.

You don't need an Idempotency-Key here: repeating the same file gives the same result. After publishing, an upload answers 400 «Места можно загрузить только до публикации» (seats can be uploaded only before publishing).

List seats

GET/v1/events/{id}/seats

ParameterDescription
limitSeats per page, 1 to 5,000, 1,000 by default
afterid of the last seat on the previous page
sectorOnly this sector

Request

curl "https://api.budu.events/v1/events/0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60/seats?limit=2" \
  -H "Authorization: Bearer $BUDU_KEY"

Response 200

{
  "seats": [
    {
      "id": "0193e0b3-0a11-7f00-8d2b-41c6e9a7b001",
      "tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c73",
      "sector": "Партер",
      "row": "1",
      "number": "1",
      "label": "П-1-1",
      "external_id": "hall-0001",
      "position": 1
    },
    {
      "id": "0193e0b3-0a11-7f00-8d2b-41c6e9a7b002",
      "tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c73",
      "sector": "Партер",
      "row": "1",
      "number": "2",
      "label": "Партер-1-2",
      "external_id": "hall-0002",
      "position": 2
    }
  ],
  "last": false,
  "sectors": ["Партер", "Балкон, левая сторона"]
}

Seats come in file order. For the next page pass after=<id of the last seat> until last turns true. sectors comes on the first page only.

Seat states

GET /v1/events/{id}/availability?seats=1 adds a page of seat states to availability: seats: [{seat_id, tariff_id, sector, label, state}] and seats_last. You page through them the same way, with the seats_after and seats_limit parameters.

stateMeaning
freeFree
heldHeld by an order's reservation. Frees up if the reservation is cancelled or expires
soldSold. Turns free again if the ticket is cancelled and returned to sale

Order a seat

In POST /v1/orders and POST /v1/reservations an item with a seat looks like {"seat": "<reference>"} instead of {"tariff_id", "qty"}:

json

{
  "event_id": "0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60",
  "items": [
    {"seat": "П-1-1"},
    {"seat": "hall-0002"},
    {"tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c71", "qty": 2}
  ],
  "buyer": {
    "email": "anna@example.ru",
    "first_name": "Анна",
    "last_name": "Петрова"
  }
}
  • A seat reference is its id, external_id or label. We check them in that order.
  • A seat's qty is 0 or 1. You can leave out tariff_id, and if you give it, it must match the seat's tier.
  • A tier with seats can't be ordered by quantity: 400 «Для этого тарифа выберите места» (choose seats for this tier).
  • A seat counts as one ticket toward the limit of 20 per order and the tier's per_order_limit.
  • In orders and tickets a seat comes as a seat: {id, sector, row, number, label} object. The guest sees "Sector · Row · Seat" on the ticket, in the email and in the calendar entry.

Errors: 409 seat_taken if another order holds the seat or it's sold (details.seat_id, details.seat). 404 not_found if the event has no such seat (details.seat). 409 event_not_on_sale with «Продажа мест ещё не открыта» (seat sales aren't open yet) if only a few seconds have passed since publishing and the seats are still being loaded into sales.