Разделы документации
Заказы
Заказ выпускает билеты. Отправьте его одним запросом после оплаты или придержите билеты резервом на время оплаты.
Есть два способа продать билет.
- Один запрос после оплаты. Покупатель оплатил у вас, сайт отправляет
POST /v1/ordersи получает билеты. Подходит, если билетов с запасом и вы готовы вернуть деньги, когда билеты закончатся за время оплаты. - Резерв на время оплаты. Сайт резервирует билеты до оплаты (
POST /v1/reservations), покупатель платит, сайт подтверждает резерв (POST /v1/reservations/{id}/confirm). Если оплата не прошла, резерв отменяют (DELETE /v1/reservations/{id}) или он истекает сам. Так продают места в зале и тарифы, которые могут закончиться.
В обоих случаях билеты выпускаются, а письмо гостю уходит сразу после подтверждения.
Объект заказа
| Поле | Тип | Описание |
|---|---|---|
id | string | UUID заказа |
org_id | string | UUID организации |
livemode | boolean | Боевой заказ или тестовый |
event_id | string | UUID события |
status | string | Статус, см. ниже |
buyer | object | Покупатель: email, first_name, last_name, phone. Он же владелец билетов |
items | array | Состав: {tariff_id, qty, seat}. Билеты без мест сгруппированы по тарифу, каждое место отдельной позицией с qty: 1 и seat (id места) |
hold_expires_at | string | До какого момента держится резерв, RFC 3339 |
payment_ref | string | Ваш номер платежа, если передали |
tickets | array | Билеты: {id, url, tariff_id, holder_first_name, holder_last_name, status, seat}. Пусто, пока билеты не выпущены |
seats | array | Места заказа {id, sector, row, number, label} в порядке позиций |
reason | string | Почему заказ нельзя подтвердить. seat_taken: место ушло другому заказу, деньги нужно вернуть |
source | string | Откуда заказ: api, manual (выдан вручную) или import (из списка гостей) |
tickets[].url — ссылка на страницу билета: https://budu.events/t/…. Её можно сразу показать покупателю. Подробнее в разделе билеты.
Статусы
| Статус | Что значит |
|---|---|
reserved | Билеты в резерве до hold_expires_at, оплаты ещё нет |
confirming | Подтверждение идёт. Отменить заказ уже нельзя |
paid | Продажа зафиксирована, билеты выпускаются. Обычно это доли секунды |
issued | Билеты выпущены, письмо отправлено |
expired | Резерв истёк. Подтверждение ещё возможно, если билеты остались |
cancelled | Резерв отменён |
failed | Подтвердить не удалось: состав не совпал с резервом. Создайте новый заказ |
Заказ в paid или issued больше не становится expired или cancelled. Отменить проданное можно только по билету: POST /v1/tickets/{id}/cancel.
Создать заказ
POST/v1/orders
Резерв и подтверждение одним запросом. Отправляйте после оплаты.
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
event_id | string | да | UUID опубликованного события |
items | array | да | Позиции: {"tariff_id", "qty"} или {"seat"} для места. Всего до 20 билетов |
buyer.email | string | да | Почта: на неё уйдёт письмо с билетами |
buyer.first_name, buyer.last_name | string | да | Имя и фамилия: они напечатаны на билете |
buyer.phone | string | нет | Телефон, хранится в заказе |
payment_ref | string | нет | Ваш номер платежа, вернётся в заказе |
Запрос
curl https://api.budu.events/v1/orders \
-H "Authorization: Bearer $BUDU_KEY" \
-H "Idempotency-Key: order-1042" \
-H "Content-Type: application/json" \
-d '{
"event_id": "0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60",
"items": [{"tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c71", "qty": 2}],
"buyer": {
"email": "maria@example.ru",
"first_name": "Мария",
"last_name": "Соколова"
},
"payment_ref": "pay-77812"
}'Ответ 201
{
"id": "0193e0c5-1f20-7c3e-b6a1-9d4e2f7a8b10",
"org_id": "0193d9a1-2b3c-7d4e-8f50-61728394a5b6",
"livemode": false,
"event_id": "0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60",
"status": "issued",
"buyer": {
"email": "maria@example.ru",
"first_name": "Мария",
"last_name": "Соколова",
"phone": ""
},
"items": [
{"tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c71", "qty": 2, "seat": ""}
],
"hold_expires_at": "2026-10-22T12:15:04Z",
"payment_ref": "pay-77812",
"tickets": [
{
"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": "valid",
"seat": null
},
{
"id": "0193e0c5-1f6b-7e02-a3c9-5b8d1e4fa21c",
"url": "https://budu.events/t/9pLmZ2xQw8RkT4vNb6CyJe",
"tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c71",
"holder_first_name": "Мария",
"holder_last_name": "Соколова",
"status": "valid",
"seat": null
}
],
"seats": [],
"reason": "",
"source": "api"
}Если билеты не успели выпуститься за пару секунд, ответ будет 202:
Ответ 202
{"status": "processing", "order_id": "0193e0c5-1f20-7c3e-b6a1-9d4e2f7a8b10"}Продажа уже зафиксирована, билеты выпустятся сами, письмо уйдёт как обычно. Ссылки на билеты появятся в GET /v1/orders/{id}: читайте заказ раз в секунду, пока статус не станет issued.
| Ошибка | Когда |
|---|---|
400 invalid_request | Нет покупателя, неверная почта, нет имени или фамилии, пустой состав, нет такого тарифа (details.tariff_id) |
404 not_found | Нет события или места |
409 event_not_on_sale | Событие не опубликовано, или продажа ещё открывается после публикации |
409 sold_out | Билеты тарифа закончились (details.tariff_id) |
409 seat_taken | Место уже занято (details.seat_id, details.seat) |
422 limit_exceeded | Больше 20 билетов или больше per_order_limit тарифа (details.tariff_id) |
500 internal с details.order_id | Связь с хранилищем остатков оборвалась посреди продажи. Заказ мы завершим сами: проверьте его через GET /v1/orders/{id} |
Зарезервировать
POST/v1/reservations
Те же поля, что у заказа, и ещё одно:
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
hold_ttl_seconds | integer | нет | Сколько держать резерв, от 60 до 1 800 секунд. По умолчанию 900, то есть 15 минут |
payment_ref в резерве не нужен: его передают при подтверждении.
Запрос
curl https://api.budu.events/v1/reservations \
-H "Authorization: Bearer $BUDU_KEY" \
-H "Idempotency-Key: cart-5531" \
-H "Content-Type: application/json" \
-d '{
"event_id": "0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60",
"items": [{"seat": "П-1-1"}, {"seat": "П-1-2"}],
"buyer": {
"email": "anna@example.ru",
"first_name": "Анна",
"last_name": "Петрова"
},
"hold_ttl_seconds": 600
}'Ответ 201 с заказом в статусе reserved и hold_expires_at. Пока резерв жив, эти билеты и места никто другой не купит. Ошибки те же, что у заказа.
Подтвердить резерв
POST/v1/reservations/{id}/confirm
Вызывайте после оплаты. Тело необязательно: {"payment_ref": "pay-77813"}.
Запрос
curl https://api.budu.events/v1/reservations/0193e0c5-2a40-7b11-9c3d-4e5f6a7b8c90/confirm \
-H "Authorization: Bearer $BUDU_KEY" \
-H "Idempotency-Key: pay-77813" \
-H "Content-Type: application/json" \
-d '{"payment_ref": "pay-77813"}'| Ситуация | Ответ |
|---|---|
| Резерв жив, билеты выпущены | 200, заказ в статусе issued с билетами |
| Продажа зафиксирована, билеты ещё выпускаются | 202 {"status": "processing", "order_id"}. Билеты появятся сами |
| Резерв истёк, но билеты ещё есть | резерв берётся заново, дальше как в первых двух строках |
| Резерв истёк, и билеты разобрали | 409 reservation_expired: верните деньги покупателю |
| Место из заказа ушло другому заказу | 409 seat_taken с details.order_id, details.seat_id, details.seat. Это окончательно: заказ становится expired с reason: "seat_taken", верните деньги |
| Резерв отменён | 400 invalid_request «Резерв отменён — создайте новый» |
Заказ уже подтверждён с другим payment_ref | 409 idempotency_conflict, в details.payment_ref прежний платёж |
Подтверждение безопасно повторять: повтор вернёт тот же заказ и те же билеты.
Успешное подтверждение побеждает. Если два подтверждения или подтверждение и истечение резерва пересеклись, а продажа успела зафиксироваться, заказ станет paid, и билеты выпустятся. Проданное мы не откатываем. Поэтому не отменяйте платёж из-за таймаута своего HTTP-клиента: повторите подтверждение с тем же Idempotency-Key или прочитайте заказ. 409 reservation_expired и 409 seat_taken приходят, только если продажа не состоялась.
Отменить резерв
DELETE/v1/reservations/{id}
Освобождает билеты и места резерва, если оплата не прошла. Подтверждённый заказ так не отменить.
Запрос
curl -X DELETE https://api.budu.events/v1/reservations/0193e0c5-2a40-7b11-9c3d-4e5f6a7b8c90 \
-H "Authorization: Bearer $BUDU_KEY"Ответ 200 с заказом в статусе cancelled. Повторная отмена отвечает так же. Ошибки: 400 invalid_request «Заказ уже оплачивается — отменить резерв нельзя» (статус confirming) и «Заказ уже оплачен — отменяйте билеты по отдельности» (paid, issued).
Получить заказ
GET/v1/orders/{id}
Запрос
curl https://api.budu.events/v1/orders/0193e0c5-1f20-7c3e-b6a1-9d4e2f7a8b10 \
-H "Authorization: Bearer $BUDU_KEY"Ответ 200 с заказом. Здесь всегда 200, даже пока билеты выпускаются: тогда статус paid, и в tickets ещё не все билеты.