# Universal

Universal Orlando tickets. You read the catalog, ask for the price of a product for a range of dates, book with the passenger list, and download the voucher.

This guide explains the flow. Field-by-field detail is in the [Universal reference](/developers/reference/v1/universal).

| Step | Endpoint | Gives you |
|---|---|---|
| 1 | `POST /api/v1/universal/getTickets` | Products and their `plu` codes |
| 2 | `POST /api/v1/universal/getTicketPrice` | Price and availability by date for one `plu` |
| 3 | `POST /api/v1/universal/ticketConfirm` | `id_order` and the voucher link |
| 4 | `GET /api/v1/universal/pdf` | The voucher PDF again, at any time |
| 5 | `POST /api/v1/universal/ticketCancelation` | Cancels the order |

All calls need your Bearer credential. Send it in the header. See [Authentication](/developers/guides/authentication).

## 1. List the catalog

```bash
curl -X POST "https://highstartravel.com/api/v1/universal/getTickets" \
  -H "Authorization: Bearer YOUR_CREDENTIAL" \
  -H "Content-Type: application/json" \
  -d '{}'
```

The response is a JSON array with one object per product. It has no prices.

```json
[
  {
    "plu": "110117001386",
    "productName": "Example Express Pass",
    "allowedDeliveryMethods0": "92",
    "allowedDeliveryMethods1": "53",
    "numberOfDays": 1,
    "isParkToPark": "0",
    "ageValue": "",
    "isLimitedExpress": "1",
    "isUnlimitedExpress": "0",
    "residencyRequirement": "",
    "isThemeParkAccess": "1",
    "themeParkAccessNames0": "USF",
    "isGradEventAccess": "0"
  }
]
```

- `plu` is the product code you use in the next calls.
- The flags, such as `isParkToPark`, are the strings `"1"` and `"0"`.
- `allowedDeliveryMethods0`, `allowedDeliveryMethods1` and so on list the delivery methods the product accepts. You need one of them at confirmation. See [Delivery method](#delivery-method).
- The API does not filter by park. Each product declares its own access, for example in `themeParkAccessNames0`.

The catalog changes rarely, so cache it. It is the same for every partner.

## 2. Read the price by date

```bash
curl -X POST "https://highstartravel.com/api/v1/universal/getTicketPrice" \
  -H "Authorization: Bearer YOUR_CREDENTIAL" \
  -H "Content-Type: application/json" \
  -d '{
    "plu": "110117001386",
    "from_date": "2026-06-15",
    "to_date": "2026-06-20"
  }'
```

```json
{
  "eventResults": [
    {
      "eventId": 12345,
      "eventDateTime": "2026-06-15T08:00:00-04:00",
      "capacityAvailable": 500,
      "totalPriceWithTax": 135.0
    }
  ],
  "request_id": "3f8a9b7c1d2e4f5a6b7c8d9e0f1a2b3c"
}
```

- Each row of `eventResults` is an available date (and time slot, if the product has them), with the capacity left and the price.
- `totalPriceWithTax` is the price of one ticket for your account. The price is per ticket, not per order.
- A product with no availability returns an empty `eventResults`.
- Ask for one `plu` at a time.
- Prices change. A price has no guaranteed lifetime. Ask right before you confirm.

## 3. Book

```bash
curl -X POST "https://highstartravel.com/api/v1/universal/ticketConfirm" \
  -H "Authorization: Bearer YOUR_CREDENTIAL" \
  -H "Content-Type: application/json" \
  -d '{
    "plu": "110117001386",
    "adults": 2,
    "children": 1,
    "date": "2026-06-15",
    "total_amount": 405.00,
    "DeliveryMethod": "92",
    "external_reference": "MY-SYSTEM-9999",
    "pax": [
      { "name": "John", "surname": "Doe", "type": "adult" },
      { "name": "Jane", "surname": "Doe", "type": "adult" },
      { "name": "Kid", "surname": "Doe", "type": "child" }
    ]
  }'
```

| Field | Rule |
|---|---|
| `plu` | The product code. If the product has separate adult and child codes, send `plu_ad` and `plu_ch` instead. You need at least one of `plu`, `plu_ad` and `plu_ch`. |
| `adults`, `children` | Integers from 0 to 50. You need at least one passenger. |
| `date` | The date of the service, `YYYY-MM-DD`. |
| `event_time` | Optional. The time slot, when the product has slots. |
| `total_amount` | The total price you expect. See below. |
| `DeliveryMethod` | Required. See [Delivery method](#delivery-method). The key is written with capital letters. |
| `pax` | One object per passenger. The count must be exactly `adults + children`. |
| `external_reference` | Your reference. Always send it. See [Conventions](/developers/guides/conventions#idempotency). |

Each passenger needs `name`, `surname` and `type` (`adult` or `child`). You can also send `email`, `phone`, `nationality`, `date` and `age`.

### Total amount

The API calculates the price again and compares it with your `total_amount`. If it differs by 0.01 or more you get `400` with `Price changed.`, and nothing is booked. The price is applied per ticket, so:

```text
total_amount = adults × adult ticket price + children × child ticket price
```

Use the `totalPriceWithTax` you read from `getTicketPrice` for each code you send, and read it right before you confirm. If there is no availability for the date you get `400` with `No availability for the selected date.`.

### Delivery method

`DeliveryMethod` must be one of the methods the product lists in `allowedDeliveryMethods`. The two usual ones are:

- `92`: the tickets are issued immediately.
- `53`: voucher delivery. Your account needs voucher sales enabled. If a product offers both `92` and `53` and your account does not sell vouchers, `53` is refused and you must use `92`.

A method that the product does not list returns `400 Delivery method not supported`, and the `message` lists the ones that are allowed. A refused voucher method returns `400 Delivery method not available`.

### The response

```json
{
  "success": true,
  "code": 200,
  "id_order": 58740,
  "pdf_voucher": "https://highstartravel.com/archivos/api/universal/pdf/Ab12Cd34Ef56.pdf"
}
```

- Keep `id_order`. You need it to cancel and to ask for the voucher again.
- The success body has no `request_id`. Keep your own `external_reference` and the `id_order` in your logs.
- `pdf_voucher` is a link that can be opened without a credential. Treat it as private. If the file is not ready yet, it is created when you open the link.

If the supplier refuses the booking you get `400`, and `error` has the supplier's reason. The order is cancelled on our side, and you can repeat the booking.

### If the request times out

Repeat the request with the same `external_reference`. You get the same order with `"idempotent_replay": true` (`id_order` is a string, as in the first answer), or `409 ORDER_CONFIRMATION_IN_PROGRESS` if the first one is still running. Do not use a new reference. If you did not send a reference, a repeat can book twice. See [Conventions](/developers/guides/conventions#idempotency).

## 4. Download the voucher

```bash
curl "https://highstartravel.com/api/v1/universal/pdf?order_id=58740" \
  -H "Authorization: Bearer YOUR_CREDENTIAL" \
  -o voucher.pdf
```

| Status | `error` | Meaning |
|---|---|---|
| 404 | `ORDER_NOT_FOUND` | The order does not exist or is not yours. |
| 410 | `ORDER_CANCELLED` | The order was cancelled. The voucher is not valid. |
| 409 | `VOUCHER_NOT_GENERATED` | The order has no tickets yet. Try again shortly. |
| 500 | `Failed to generate PDF` | The file could not be created. Retry, then contact support. |

## 5. Cancel

```bash
curl -X POST "https://highstartravel.com/api/v1/universal/ticketCancelation" \
  -H "Authorization: Bearer YOUR_CREDENTIAL" \
  -H "Content-Type: application/json" \
  -d '{ "id_order": 58740 }'
```

A successful cancellation returns `{"success": true}` and cancels every ticket of the order.

An order can hold several tickets and they are cancelled one by one. If one of them cannot be cancelled, the ones already cancelled stay cancelled, and the answer is `400` with `Can't cancel order`. The answer does not say which ones were cancelled. Contact support with your `id_order` so they can reconcile it.

| Status | `error` or `message` | Meaning |
|---|---|---|
| 400 | `Can't cancel this ticket.` | The order is not yours, or it is not active. This answer has only a `message` field. |
| 400 | `Ticket not found.` | The order has no Universal ticket that can be cancelled. |
| 400 | `Can't cancel order` | The supplier refused, or only some tickets were cancelled. |

## Errors to handle

| Status | `error` | Action |
|---|---|---|
| 400 | `Price changed.` | Ask for the price again and confirm with the new total. |
| 400 | `No availability for the selected date.` | Choose another date or product. |
| 400 | `Missing Product ID` | Send `plu`, or `plu_ad` or `plu_ch`. |
| 400 | `Pax count mismatch`, `Invalid pax entry` | Make the passenger list match the counts, with `name`, `surname` and `type` in each. |
| 400 | `Ticket not found.` | The `plu` does not match a product that is on sale (a product that exists but is not on sale gets the same answer). Check it against the catalog. |
| 404 | `Product not found` | In `getTicketPrice`, the `plu` does not match a product for sale. |
| 502 | `External API error` | A supplier did not answer. Retry with backoff. |
| 503 | `Service unavailable` | A supplier service is not available. Retry in a few seconds. |

The response to a missing required field in `getTicketPrice` has the label `Faltan datos requeridos`, in Spanish. The status is `400` and `message` is `Empty fields received`. See [Errors](/developers/guides/errors) for the shape of the Universal answers.

## What not to do

- Do not reuse a price from an earlier query. Ask right before you confirm.
- Do not retry a timed-out confirmation with a new `external_reference`.
- Do not rely on the `pdf_voucher` link as the only copy. You can ask for the voucher again by `id_order`.

## Next

- [Universal reference](/developers/reference/v1/universal) has every field and response.
- [Sandbox](/developers/guides/sandbox) explains how to test a booking.
