# Tickets

Ticket links for the guest, search, cancel, QR reissue, email resend and a CSV export of an event's tickets.

One ticket means one guest and one QR. Tickets come from an [order](https://budu.events/en/docs/orders/), [a manual issue or a guest list](https://budu.events/en/docs/issue/).

## Ticket object {#ticket-object}

| Field | Type | Description |
|---|---|---|
| `id` | string | Ticket UUID |
| `org_id`, `livemode` | string, boolean | Organization and mode |
| `order_id`, `order_item_id` | string | Order and order item |
| `event_id`, `tariff_id` | string | Event and tier |
| `holder_first_name`, `holder_last_name`, `holder_email` | string | Ticket holder |
| `status` | string | `valid` or `cancelled` |
| `qr_version` | integer | QR version, goes up with each reissue |
| `public_token` | string | The secret in the ticket link: `https://budu.events/t/<public_token>` |
| `qr_payload` | string | What the QR encodes: a signed `BT1:…` string |
| `seat` | object | Seat `{id, sector, row, number, label}` or `null` |
| `created_at_ms` | string | When it was issued, Unix milliseconds |
| `number` | string | The number on the ticket: the last 8 characters of `id` in upper case, such as `1E4F9F0A`. The guest and door staff find the ticket by it |
| `checked_in_at_ms` | string | When the guest checked in. `"0"`: not checked in |
| `entrance` | string | The entrance where the guest checked in, that is the door staff device's name. Empty: not checked in |

> `public_token` and `qr_payload` get someone into the event. Keep them out of logs, never send them to third parties, and show them only to the ticket holder.

## Guest links {#links}

The links work without signing in or signing up. We send them to the guest by email, and you can show them on your site right after payment.

| URL | What's there |
|---|---|
| `https://budu.events/t/<token>` | Ticket page: QR, event, seat, directions, «В календарь» (add to calendar) |
| `https://budu.events/t/<token>/qr.svg`, `/qr.png` | The QR code as a picture |
| `https://budu.events/t/<token>/ticket.png` | The ticket as one picture, to save to the photo gallery |
| `https://budu.events/t/<token>/event.ics` | The event for a calendar |
| `https://budu.events/o/<token>` | Every ticket of the order on one page. The email has this link when the order holds several tickets |

`<token>` is the ticket's `public_token`. An order already gives you the full ticket link: `tickets[].url`. After a [reissue](#reissue) the old links answer `410` «Билет перевыпущен» (ticket reissued).

## List tickets {#list}

`GET /v1/tickets`

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

The organization's tickets in the key's mode, or one event's tickets, newest first. Each ticket shows whether the guest checked in.

| Parameter | Description |
|---|---|
| `event_id` | Only this event's tickets (for `GET /v1/tickets`) |
| `q` | Search: first and last name in either order, email, ticket number or seat label. 2 to 100 characters |
| `status` | `valid` or `cancelled` |
| `limit` | Tickets per page, 1 to 200, 50 by default |
| `after` | The `next` value from the previous response |

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

```json Response 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` is empty on the last page.

## Get a ticket {#get}

`GET /v1/tickets/{id}`

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

You get `200` with the [ticket](#ticket-object). Here `checked_in_at_ms` and `entrance` are always empty: check-ins show in the [ticket list](#list) and in the [check-in report](https://budu.events/en/docs/devices/#checkin).

## Cancel a ticket {#cancel}

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

The QR stops working at the door and the guest gets a cancellation email. You handle the refund.

| Field | Type | Description |
|---|---|---|
| `return_to_sale` | boolean | `true` (default): the ticket or seat goes back on sale. `false`: stock stays as it is |

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

You can leave the body out. An unknown field, such as the typo `retrun_to_sale`, gives `400`, so a return to sale never switches on by mistake. Cancelling again answers the same.

## Reissue {#reissue}

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

A new QR and a new link, for when the ticket went to the wrong person or the QR got into the wrong hands. The old QR stops working at the door and the old link answers «Билет перевыпущен» (ticket reissued). The guest gets an email with the new ticket.

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

You get `200` with the [ticket](#ticket-object): new `public_token` and `qr_payload`, `qr_version` up by 1. Errors: `400` «Отменённый билет нельзя перевыпустить» (a cancelled ticket can't be reissued), `429` when reissuing more than once a minute.

## Resend the email {#resend}

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

The ticket email goes to the holder once more, at the same address.

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

```json Response 202
{"status": "queued"}
```

Once a minute per ticket at most, otherwise `429 rate_limited`. In test mode the email goes out only if the address is among the [test recipients](https://budu.events/en/docs/test-mode/#recipients).

## Export to CSV {#export}

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

Every ticket of the event in one file for Excel: UTF-8 with a BOM, `;` as the separator, times in the event's time zone.

```bash Request
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
```

A value that starts with `=`, `+`, `-` or `@` gets a leading apostrophe, so Excel doesn't take it for a formula.
