# Issue without payment

Tickets for guests, partners and press: one at a time by request, or as a CSV guest list of up to 2,000 lines.

A manual issue goes through the same hold as a sale: quotas, seats, `sold_out` and `seat_taken` work the same way. There's no payment, the ticket holder becomes the order's buyer, and the order's `source` is `manual` or `import`.

## Issue tickets {#issue}

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

`Idempotency-Key` is required: a retry after a dropped connection won't issue a second set.

| Field | Type | Required | Description |
|---|---|---|---|
| `items` | array | yes | Items, as in an order: `{"tariff_id", "qty"}` or `{"seat"}` |
| `holder.email` | string | yes | The holder's email: the ticket email goes there |
| `holder.first_name`, `holder.last_name` | string | yes | First and last name on the ticket |
| `holder.phone` | string | no | Phone |

```bash Request
curl https://api.budu.events/v1/events/0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60/issue \
  -H "Authorization: Bearer $BUDU_KEY" \
  -H "Idempotency-Key: press-kommersant-1" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [{"tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c71", "qty": 2}],
    "holder": {
      "email": "press@example.ru",
      "first_name": "Ольга",
      "last_name": "Миронова"
    }
  }'
```

You get `201` with the [order](https://budu.events/en/docs/orders/#order-object) and its tickets, `source: "manual"`. While the tickets are being issued, the response is `202 {"status": "processing", "order_id"}`. The errors are the same as for an [order](https://budu.events/en/docs/orders/#create), plus `400` if there's no `Idempotency-Key`.

## Upload a guest list {#import}

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

The body is a CSV file, `Content-Type: text/csv`. One line is one guest and their tickets.

```csv
first_name,last_name,email,tariff,seat,qty
Ольга,Миронова,press@example.ru,Танцпол,,2
Игорь,Белов,igor@example.ru,Партер,П-1-1,
```

| Column | Required | Description |
|---|---|---|
| `first_name`, `last_name`, `email` | yes | The guest |
| `tariff` | if there's more than one tier | Tier name or code |
| `seat` | for a tier with seats | The seat: label, `external_id` or `id`. A line with a seat is one ticket |
| `qty` | no | How many tickets, 1 to 20. Empty means 1 |

- Headers can be in Russian: `имя`, `фамилия`, `почта`, `тариф`, `место`, `количество`.
- The separator is `,` or `;`, UTF-8, a BOM and Windows line breaks are fine.
- Up to 2,000 lines and 1 MB.
- If two tiers share a name, give the code.

`?dry_run=1` checks the file and counts the tickets without issuing anything. You get `200`:

```json Response 200
{
  "id": "",
  "event_id": "0193e0b2-4a7c-7d10-9e3f-2c5a8b1d4e60",
  "status": "",
  "lines": 2,
  "tickets": 3,
  "issued": 0,
  "failed": 0,
  "errors": [],
  "tariffs": [
    {
      "tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c71",
      "tickets": 2,
      "available": 79
    },
    {
      "tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c73",
      "tickets": 1,
      "available": 140
    }
  ],
  "warnings": [],
  "created_at_ms": "0",
  "finished_at_ms": "0",
  "repeated_file": false,
  "guests_with_tickets": 0
}
```

Without `dry_run` the import starts: you get `201`, `status: "running"` and the import's `id`. Tickets are issued in the background, and guests get their emails as their tickets come out.

- Errors in the file: `400 invalid_request`, the list in `details.errors: [{line, message}]`. Nothing is issued.
- The same list for the same event: `200` with the earlier import and `repeated_file: true`. No tickets are issued the second time. We compare the file line by line, so a file re-saved in Excel counts as the same.
- `guests_with_tickets` and `warnings` warn but don't stop the import: for example, some guests already hold tickets for this event, or fewer tickets are free than the file asks for.

## Import progress {#import-get}

`GET /v1/imports/{id}`

```bash Request
curl https://api.budu.events/v1/imports/0193e0d1-7c00-7e5f-a1b2-c3d4e5f60718 \
  -H "Authorization: Bearer $BUDU_KEY"
```

`status`: `running` or `done`. `issued` and `failed` count how many of `lines` were issued and how many failed. `errors` lists the lines that couldn't be issued, with the reason and the guest: `{line, message, first_name, last_name, email}`. The first 100, or all of them with `?errors=all`.

## An event's imports {#import-list}

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

The event's last 20 imports, newest first: `{"imports": [...]}`.
