---
name: dubailand-travel-api
description: Integrate with the DubaiLand Travel API for flights, hotels and transfers — search, confirm a price, place and manage orders. Use when writing or debugging code that calls https://staging.dubailandtravel.com/partner/v1.
---

Rules that are easy to get wrong:

- Always call `confirm-price` immediately before `POST /orders`, and pass its `price_confirmation_token`.
- Send an `Idempotency-Key` on `POST /orders`, and reuse the SAME key when retrying after a timeout. Never generate a new one for a retry: that books twice.
- Develop against a sandbox key (`sk_test_…`). It reaches no supplier and moves no money.
- Branch on `error.code`, not on the message text.
- Never put the client secret in browser or mobile code. Call this API from a server.

# DubaiLand Travel API

Flights, hotels and transfers for businesses, over one JSON API. Orders settle against a prepaid credit balance.

- Base URL: `https://staging.dubailandtravel.com/partner/v1`
- Authentication: HTTP Basic, client id as the username and secret as the password. `Authorization: Bearer client_id:secret` also works.
- Environment: decided by the key. `sk_test_…` is sandbox (no supplier, no money); `sk_live_…` is live.
- Currency: everything is in USD. A request naming another currency is refused, not converted.
- Mutating calls that create an order need an `Idempotency-Key` header (any unique string, e.g. a UUID). Reuse the same key when retrying.

## The flow

1. **Search** — `POST /flights/search` (or `/hotels/search`, `/transfers/search`). Take an offer `id` from `data.offers`.
2. **Confirm the price** — `POST /flights/offers/{offerId}/confirm-price`. Take `data.price_confirmation_token`. Prices expire in minutes; always do this immediately before ordering.
3. **Order** — `POST /orders` with the offer id, the token and the travellers.

```bash
# 1. Search
curl -u "$CLIENT_ID:$CLIENT_SECRET" -X POST https://staging.dubailandtravel.com/partner/v1/flights/search \
  -H 'Content-Type: application/json' \
  -d '{"slices":[{"origin":"BGW","destination":"DXB","date":"2026-12-02"}],"passengers":{"adults":1}}'

# 2. Confirm the price
curl -u "$CLIENT_ID:$CLIENT_SECRET" -X POST https://staging.dubailandtravel.com/partner/v1/flights/offers/$OFFER_ID/confirm-price

# 3. Order
curl -u "$CLIENT_ID:$CLIENT_SECRET" -X POST https://staging.dubailandtravel.com/partner/v1/orders \
  -H 'Content-Type: application/json' -H "Idempotency-Key: $(uuidgen)" \
  -d @order.json
```

`order.json`:

```json
{
    "items": [
        {
            "type": "flight",
            "offer_id": "{{offer_id}}",
            "price_confirmation_token": "{{price_confirmation_token}}"
        }
    ],
    "passengers": [
        {
            "type": "ADT",
            "first_name": "Dana",
            "last_name": "Aziz",
            "date_of_birth": "1990-04-02",
            "gender": "F",
            "nationality": "IQ",
            "is_lead": true,
            "document": {
                "type": "passport",
                "number": "A1234567",
                "issuing_country": "IQ",
                "expires_on": "2031-11-02"
            }
        }
    ],
    "contact": {
        "email": "traveller@example.com",
        "phone": "+9647701234567"
    },
    "partner_reference": "your-own-id"
}
```

## Responses

Success is `{"data": …, "meta": {…}}`. Failure is `{"error": {"code": "TB-…", "title": …, "detail": …}}` with a matching HTTP status. Error codes are stable; branch on `error.code`, never on the text.

A `429` carries `Retry-After`: wait that long rather than retrying immediately.

## Endpoints

### GET /account

The account at a glance

Response shape (values are placeholders):

```json
{
    "data": {
        "partner": {
            "name": "string",
            "status": "string",
            "account_id": "string"
        },
        "credential": {
            "label": "string",
            "environment": "string",
            "scopes": [
                "string"
            ],
            "expires_at": "string"
        },
        "services": [
            "string"
        ],
        "usage_this_period": {
            "period": "string",
            "calls": 0,
            "remaining": 0
        },
        "balances": [
            "string"
        ]
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string"
    }
}
```

### GET /account/usage

Usage for a closed or current period

Response shape (values are placeholders):

```json
{
    "data": {
        "period": "string",
        "calls": 0,
        "billable_calls": 0,
        "overage_calls": 0,
        "errors": 0,
        "p95_duration_ms": 0
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "billable": true
    }
}
```

### GET /account/statement

Every movement on the account in a period, with a running balance

The running balance is what makes this reconcilable line by line rather than only in total — a partner whose closing figure disagrees with ours needs to find WHERE, not merely THAT.

Response shape (values are placeholders):

```json
{
    "data": {
        "period": "string",
        "currency": "string",
        "opening_balance": {},
        "closing_balance": {},
        "credits": {},
        "debits": {},
        "entries": [
            "string"
        ]
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string"
    }
}
```

### POST /flights/search

Search flights

Returns offers already priced for the calling partner. A supplier failing degrades the search rather than failing it.

| Field | Type | Required |
|---|---|---|
| `trip_type` | string: one_way, round_trip, multi_city | no |
| `slices` | array | yes |
| `slices[].origin` | string | yes |
| `slices[].destination` | string | yes |
| `slices[].date` | string | yes |
| `passengers` | object | yes |
| `passengers.adults` | integer | yes |
| `passengers.children` | integer | no |
| `passengers.infants` | integer | no |
| `cabin` | string: ECONOMY, PREMIUM_ECONOMY, BUSINESS, FIRST | no |
| `preferences` | object | no |
| `preferences.non_stop_only` | boolean | no |
| `preferences.airlines` | array | no |
| `preferences.exclude_airlines` | array | no |
| `preferences.max_price` | integer | no |
| `currency` | string | no |

```json
{
    "slices": [
        {
            "origin": "BGW",
            "destination": "DXB",
            "date": "2026-12-02"
        }
    ],
    "passengers": {
        "adults": 1
    }
}
```

Response shape (values are placeholders):

```json
{
    "data": {
        "offers": [
            {
                "id": "string",
                "total": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "base": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "taxes": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "currency": "string",
                "validating_carrier": "string",
                "cabin": "string",
                "fare_type": "string",
                "slices": [
                    "string"
                ],
                "passengers": [
                    "string"
                ],
                "conditions": {
                    "refundable": true,
                    "changeable": true,
                    "seats_remaining": 0,
                    "last_ticketing_date": "string"
                },
                "baggage": {},
                "expires_at": "string"
            }
        ]
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "search_id": "string",
        "count": 0,
        "cache_hit": "string",
        "degraded": true
    }
}
```

### GET /flights/search/{searchId}/results

Read a held search

Cheap — the offers were computed once. Paging or filtering a result set a partner already has must never cost another supplier call, and must never consume their search rate limit either.

Response shape (values are placeholders):

```json
{
    "data": {
        "offers": [
            {
                "id": "string",
                "total": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "base": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "taxes": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "currency": "string",
                "validating_carrier": "string",
                "cabin": "string",
                "fare_type": "string",
                "slices": [
                    "string"
                ],
                "passengers": [
                    "string"
                ],
                "conditions": {
                    "refundable": true,
                    "changeable": true,
                    "seats_remaining": 0,
                    "last_ticketing_date": "string"
                },
                "baggage": {},
                "expires_at": "string"
            }
        ]
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "search_id": "string",
        "count": 0
    }
}
```

### POST /flights/offers/{offerId}/confirm-price

Confirm a price

Mandatory before an order. Airfare moves between search and booking often enough that ordering from a held price is a chargeback waiting to happen — and in B2B it would be the partner absorbing it, which is worse. Returns a token bound to the offer and its final amount. Ordering without one is refused.

Response shape (values are placeholders):

```json
{
    "data": {
        "offer": {
            "id": "string",
            "total": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "base": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "taxes": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "currency": "string",
            "validating_carrier": "string",
            "cabin": "string",
            "fare_type": "string",
            "slices": [
                "string"
            ],
            "passengers": [
                "string"
            ],
            "conditions": {
                "refundable": true,
                "changeable": true,
                "seats_remaining": 0,
                "last_ticketing_date": "string"
            },
            "baggage": {},
            "expires_at": "string"
        },
        "price_changed": true,
        "previous_total": {
            "amount": 0,
            "currency": "string",
            "decimals": 0,
            "formatted": "string"
        },
        "price_confirmation_token": "string"
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string"
    }
}
```

### POST /hotels/search

Search hotels

Returns rates already priced for the calling partner. A supplier failing degrades the search rather than failing it.

| Field | Type | Required |
|---|---|---|
| `city_code` | string | no |
| `hotel_id` | string | no |
| `latitude` | number | no |
| `longitude` | number | no |
| `radius_km` | integer | no |
| `check_in` | string | yes |
| `check_out` | string | yes |
| `rooms` | integer | no |
| `adults` | integer | no |
| `children_ages` | array | no |
| `currency` | string | no |
| `min_stars` | integer | no |
| `board_type` | string: room_only, breakfast, half_board, full_board, all_inclusive | no |

```json
{
    "city_code": "DXB",
    "check_in": "2026-12-02",
    "check_out": "2026-12-05",
    "rooms": 1,
    "adults": 2
}
```

Response shape (values are placeholders):

```json
{
    "data": {
        "offers": [
            {
                "id": "string",
                "property": {
                    "id": "string",
                    "name": "string",
                    "star_rating": 0,
                    "city_code": "string",
                    "address": "string",
                    "latitude": 0,
                    "longitude": 0,
                    "amenities": [
                        "string"
                    ],
                    "images": [
                        "string"
                    ]
                },
                "room": {
                    "type_code": "string",
                    "description": {},
                    "board_type": "string",
                    "rate_plan": "string"
                },
                "stay": {
                    "check_in": "string",
                    "check_out": "string",
                    "nights": 0,
                    "rooms": 0,
                    "adults": 0,
                    "children_ages": [
                        "integer"
                    ]
                },
                "total": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "taxes_and_fees": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "per_night": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "currency": "string",
                "payment_policy": "string",
                "collected_by_property": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "payable_now": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "cancellation": {
                    "refundable": true,
                    "free_until": "string",
                    "penalty": {
                        "amount": "integer",
                        "currency": "string",
                        "decimals": "integer",
                        "formatted": "string"
                    },
                    "tiers": [
                        {}
                    ],
                    "description": "string"
                },
                "expires_at": "string"
            }
        ]
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "search_id": "string",
        "count": "string",
        "cache_hit": "string",
        "expires_at": "string",
        "degraded": true
    }
}
```

### GET /hotels/search/{searchId}/results

Read a held search

Cheap — the rates were computed once. Paging or filtering a result set a partner already holds must never cost another supplier call, and must never consume their search rate limit either.

Response shape (values are placeholders):

```json
{
    "data": {
        "offers": [
            {
                "id": "string",
                "property": {
                    "id": "string",
                    "name": "string",
                    "star_rating": 0,
                    "city_code": "string",
                    "address": "string",
                    "latitude": 0,
                    "longitude": 0,
                    "amenities": [
                        "string"
                    ],
                    "images": [
                        "string"
                    ]
                },
                "room": {
                    "type_code": "string",
                    "description": {},
                    "board_type": "string",
                    "rate_plan": "string"
                },
                "stay": {
                    "check_in": "string",
                    "check_out": "string",
                    "nights": 0,
                    "rooms": 0,
                    "adults": 0,
                    "children_ages": [
                        "integer"
                    ]
                },
                "total": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "taxes_and_fees": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "per_night": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "currency": "string",
                "payment_policy": "string",
                "collected_by_property": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "payable_now": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "cancellation": {
                    "refundable": true,
                    "free_until": "string",
                    "penalty": {
                        "amount": "integer",
                        "currency": "string",
                        "decimals": "integer",
                        "formatted": "string"
                    },
                    "tiers": [
                        {}
                    ],
                    "description": "string"
                },
                "expires_at": "string"
            }
        ]
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "search_id": "string",
        "count": 0
    }
}
```

### POST /hotels/offers/{offerId}/confirm-price

Confirm a rate

Mandatory before an order, and for more than the price: a cancellation policy that changed between search and booking is a promise we would otherwise make and not keep — and in B2B it is the partner who would have made it to their customer. Returns a token bound to the offer and its final amount. Ordering without one is refused.

Response shape (values are placeholders):

```json
{
    "data": {
        "offer": {
            "id": "string",
            "property": {
                "id": "string",
                "name": "string",
                "star_rating": 0,
                "city_code": "string",
                "address": "string",
                "latitude": 0,
                "longitude": 0,
                "amenities": [
                    "string"
                ],
                "images": [
                    "string"
                ]
            },
            "room": {
                "type_code": "string",
                "description": {},
                "board_type": "string",
                "rate_plan": "string"
            },
            "stay": {
                "check_in": "string",
                "check_out": "string",
                "nights": 0,
                "rooms": 0,
                "adults": 0,
                "children_ages": [
                    0
                ]
            },
            "total": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "taxes_and_fees": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "per_night": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "currency": "string",
            "payment_policy": "string",
            "collected_by_property": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "payable_now": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "cancellation": {
                "refundable": true,
                "free_until": "string",
                "penalty": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "tiers": [
                    {
                        "from": "string",
                        "amount_minor": "integer"
                    }
                ],
                "description": "string"
            },
            "expires_at": "string"
        },
        "price_changed": true,
        "previous_total": {
            "amount": 0,
            "currency": "string",
            "decimals": 0,
            "formatted": "string"
        },
        "policy_changed": true,
        "price_confirmation_token": "string"
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string"
    }
}
```

### POST /orders

Place an order Requires an `Idempotency-Key` header.

Requires an `Idempotency-Key`. Draws the total from the partner's balance, creates the order with the supplier, and returns the confirmation. A refusal at the supplier releases the hold immediately — the partner is never out of pocket for a booking that did not happen. A 402 means the balance and credit limit together do not cover it, and the body states the shortfall.

| Field | Type | Required |
|---|---|---|
| `items` | array | yes |
| `items[].type` | string: flight, hotel, transfer | yes |
| `items[].offer_id` | string | yes |
| `items[].price_confirmation_token` | string | yes |
| `passengers` | array | no |
| `passengers[].type` | string: ADT, CHD, INF | no |
| `passengers[].first_name` | string | yes |
| `passengers[].last_name` | string | yes |
| `passengers[].date_of_birth` | string | no |
| `passengers[].gender` | string: M, F, X | no |
| `passengers[].nationality` | string | no |
| `passengers[].is_lead` | boolean | no |
| `passengers[].document` | object | no |
| `passengers[].document.type` | string | no |
| `passengers[].document.number` | string | no |
| `passengers[].document.issuing_country` | string | no |
| `passengers[].document.expires_on` | string | no |
| `guests` | array | no |
| `guests[].first_name` | string | no |
| `guests[].last_name` | string | no |
| `guests[].is_lead` | boolean | no |
| `special_requests` | string | no |
| `flight_number` | string | no |
| `bags` | integer | no |
| `estimated_arrival_time` | string | no |
| `contact` | object | yes |
| `contact.email` | string | yes |
| `contact.phone` | string | yes |
| `contact.name` | string | no |
| `partner_reference` | string | no |

```json
{
    "items": [
        {
            "type": "flight",
            "offer_id": "{{offer_id}}",
            "price_confirmation_token": "{{price_confirmation_token}}"
        }
    ],
    "passengers": [
        {
            "type": "ADT",
            "first_name": "Dana",
            "last_name": "Aziz",
            "date_of_birth": "1990-04-02",
            "gender": "F",
            "nationality": "IQ",
            "is_lead": true,
            "document": {
                "type": "passport",
                "number": "A1234567",
                "issuing_country": "IQ",
                "expires_on": "2031-11-02"
            }
        }
    ],
    "contact": {
        "email": "traveller@example.com",
        "phone": "+9647701234567"
    },
    "partner_reference": "your-own-id"
}
```

Response shape (values are placeholders):

```json
{
    "data": {
        "order": {
            "reference": "string",
            "partner_reference": "string",
            "status": "string",
            "type": "string",
            "total": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "charged_to_balance": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "contact": {
                "email": "string",
                "phone": "string",
                "name": "string"
            },
            "passengers": [
                {
                    "first_name": "string",
                    "last_name": "string",
                    "type": "string",
                    "document_last4": "string"
                }
            ],
            "items": [
                {
                    "id": 0,
                    "type": "string",
                    "status": "string",
                    "total": {
                        "amount": "integer",
                        "currency": "string"
                    },
                    "hotel": "string",
                    "confirmation_number": "string",
                    "check_in": "string",
                    "check_out": "string"
                }
            ],
            "travel_starts_at": "string",
            "created_at": "string",
            "ticketing_deadline_at": "string"
        }
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "balance": {
            "amount": 0,
            "currency": "string",
            "decimals": 0,
            "formatted": "string"
        }
    }
}
```

### GET /orders

List orders

Query: `status`, `created_after`, `page`, `per_page`

Response shape (values are placeholders):

```json
{
    "data": {
        "orders": [
            {
                "reference": "string",
                "partner_reference": "string",
                "status": "string",
                "type": "string",
                "total": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "charged_to_balance": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "contact": {
                    "email": "string",
                    "phone": "string",
                    "name": "string"
                },
                "passengers": [
                    {
                        "first_name": "string",
                        "last_name": "string",
                        "type": "string",
                        "document_last4": "string"
                    }
                ],
                "items": [
                    {
                        "id": "integer",
                        "type": "string",
                        "status": "string",
                        "total": {},
                        "hotel": "string",
                        "confirmation_number": "string",
                        "check_in": "string",
                        "check_out": "string"
                    }
                ],
                "travel_starts_at": "string",
                "created_at": "string",
                "ticketing_deadline_at": "string"
            }
        ]
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "page": 0,
        "per_page": 0,
        "total": 0
    }
}
```

### GET /orders/{reference}

Read an order

Response shape (values are placeholders):

```json
{
    "data": {
        "order": {
            "reference": "string",
            "partner_reference": "string",
            "status": "string",
            "type": "string",
            "total": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "charged_to_balance": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "contact": {
                "email": "string",
                "phone": "string",
                "name": "string"
            },
            "passengers": [
                {
                    "first_name": "string",
                    "last_name": "string",
                    "type": "string",
                    "document_last4": "string"
                }
            ],
            "items": [
                {
                    "id": 0,
                    "type": "string",
                    "status": "string",
                    "total": {
                        "amount": "integer",
                        "currency": "string"
                    },
                    "hotel": "string",
                    "confirmation_number": "string",
                    "check_in": "string",
                    "check_out": "string"
                }
            ],
            "travel_starts_at": "string",
            "created_at": "string",
            "ticketing_deadline_at": "string"
        }
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string"
    }
}
```

### POST /orders/{reference}/cancel

Cancel an order Requires an `Idempotency-Key` header.

Returns the penalty the supplier actually charged and credits the remainder back to the partner's balance. On a prepaid model a refund is a ledger entry rather than a gateway call that can fail, which is the single biggest operational advantage of settling this way.

| Field | Type | Required |
|---|---|---|
| `reason` | string | no |

Response shape (values are placeholders):

```json
{
    "data": {
        "order": {
            "reference": "string",
            "partner_reference": "string",
            "status": "string",
            "type": "string",
            "total": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "charged_to_balance": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "contact": {
                "email": "string",
                "phone": "string",
                "name": "string"
            },
            "passengers": [
                {
                    "first_name": "string",
                    "last_name": "string",
                    "type": "string",
                    "document_last4": "string"
                }
            ],
            "items": [
                {
                    "id": 0,
                    "type": "string",
                    "status": "string",
                    "total": {
                        "amount": "integer",
                        "currency": "string"
                    },
                    "hotel": "string",
                    "confirmation_number": "string",
                    "check_in": "string",
                    "check_out": "string"
                }
            ],
            "travel_starts_at": "string",
            "created_at": "string",
            "ticketing_deadline_at": "string"
        },
        "refunded_to_balance": {
            "amount": 0,
            "currency": "string",
            "decimals": 0,
            "formatted": "string"
        },
        "penalty": {
            "amount": 0,
            "currency": "string",
            "decimals": 0,
            "formatted": "string"
        }
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "balance": {
            "amount": 0,
            "currency": "string",
            "decimals": 0,
            "formatted": "string"
        }
    }
}
```

### POST /orders/{reference}/hotels

Add a hotel to an order Requires an `Idempotency-Key` header.

Requires an `Idempotency-Key`: this changes what the partner owes, and a retry must not book the room twice. The room is reserved and confirmed on its own. A property refusing leaves the flight exactly as it was — that is the whole reason items carry independent lifecycles — and comes back as a 409 on the room rather than as a failure of the order.

| Field | Type | Required |
|---|---|---|
| `offer_id` | string | yes |
| `price_confirmation_token` | string | yes |
| `special_requests` | string | no |
| `estimated_arrival_time` | string | no |

Response shape (values are placeholders):

```json
{
    "data": {
        "order": {
            "reference": "string",
            "partner_reference": "string",
            "status": "string",
            "type": "string",
            "total": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "charged_to_balance": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "contact": {
                "email": "string",
                "phone": "string",
                "name": "string"
            },
            "passengers": [
                {
                    "first_name": "string",
                    "last_name": "string",
                    "type": "string",
                    "document_last4": "string"
                }
            ],
            "items": [
                {
                    "id": 0,
                    "type": "string",
                    "status": "string",
                    "total": {
                        "amount": "integer",
                        "currency": "string"
                    },
                    "hotel": "string",
                    "confirmation_number": "string",
                    "check_in": "string",
                    "check_out": "string"
                }
            ],
            "travel_starts_at": "string",
            "created_at": "string",
            "ticketing_deadline_at": "string"
        }
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "balance": {
            "amount": 0,
            "currency": "string",
            "decimals": 0,
            "formatted": "string"
        }
    }
}
```

### POST /orders/{reference}/transfers

Add a transfer to an order Requires an `Idempotency-Key` header.

Requires an `Idempotency-Key`: this changes what the partner owes, and a retry must not put a second car on the road. The car is booked on its own. An operator refusing leaves the flight exactly as it was and comes back as a 410 on the transfer rather than a failure of the order.

| Field | Type | Required |
|---|---|---|
| `offer_id` | string | yes |
| `flight_number` | string | no |
| `passengers` | integer | no |
| `bags` | integer | no |

Response shape (values are placeholders):

```json
{
    "data": {
        "order": {
            "reference": "string",
            "partner_reference": "string",
            "status": "string",
            "type": "string",
            "total": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "charged_to_balance": {
                "amount": 0,
                "currency": "string",
                "decimals": 0,
                "formatted": "string"
            },
            "contact": {
                "email": "string",
                "phone": "string",
                "name": "string"
            },
            "passengers": [
                {
                    "first_name": "string",
                    "last_name": "string",
                    "type": "string",
                    "document_last4": "string"
                }
            ],
            "items": [
                {
                    "id": 0,
                    "type": "string",
                    "status": "string",
                    "total": {
                        "amount": "integer",
                        "currency": "string"
                    },
                    "hotel": "string",
                    "confirmation_number": "string",
                    "check_in": "string",
                    "check_out": "string"
                }
            ],
            "travel_starts_at": "string",
            "created_at": "string",
            "ticketing_deadline_at": "string"
        }
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "balance": {
            "amount": 0,
            "currency": "string",
            "decimals": 0,
            "formatted": "string"
        }
    }
}
```

### POST /transfers/search

Quote a transfer

Returns every vehicle an operator will put on the journey, already priced for the calling partner and already filtered to those that can actually carry the party.

| Field | Type | Required |
|---|---|---|
| `origin_id` | string | yes |
| `destination_id` | string | yes |
| `pickup_at` | string | yes |
| `return_at` | string | no |
| `adults` | integer | yes |
| `children` | integer | no |
| `infants` | integer | no |
| `bags` | integer | no |
| `flight_number` | string | no |
| `currency` | string | no |

```json
{
    "origin_id": "{{origin_id}}",
    "destination_id": "{{destination_id}}",
    "pickup_at": "2026-12-02 14:30",
    "adults": 2
}
```

Response shape (values are placeholders):

```json
{
    "data": {
        "offers": [
            {
                "id": "string",
                "journey": {
                    "origin": {
                        "id": "string",
                        "name": "string",
                        "type": "string",
                        "address": "string",
                        "city_name": "string",
                        "country_code": "string",
                        "iata_code": "string",
                        "latitude": "number",
                        "longitude": "number"
                    },
                    "destination": {
                        "id": "string",
                        "name": "string",
                        "type": "string",
                        "address": "string",
                        "city_name": "string",
                        "country_code": "string",
                        "iata_code": "string",
                        "latitude": "number",
                        "longitude": "number"
                    },
                    "pickup_at": "string",
                    "pickup_timezone": "string",
                    "return_pickup_at": "string",
                    "duration_minutes": 0,
                    "distance_km": 0
                },
                "vehicle": {
                    "class": "string",
                    "description": {},
                    "max_passengers": 0,
                    "max_bags": 0,
                    "inclusions": [
                        "string"
                    ]
                },
                "total": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "currency": "string",
                "payment_policy": "string",
                "collected_by_driver": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "payable_now": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "cancellation": {
                    "refundable": true,
                    "free_until": "string",
                    "penalty": {
                        "amount": "integer",
                        "currency": "string",
                        "decimals": "integer",
                        "formatted": "string"
                    },
                    "description": "string"
                },
                "expires_at": "string"
            }
        ]
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "search_id": "string",
        "count": "string",
        "cache_hit": "string",
        "expires_at": "string",
        "degraded": true,
        "unserved": "string"
    }
}
```

### GET /transfers/search/{searchId}/results

Read a held quote

Cheap — the quotes were computed once. Re-reading a result set a partner already holds must never cost another operator call, and must never consume their search rate limit either.

Response shape (values are placeholders):

```json
{
    "data": {
        "offers": [
            {
                "id": "string",
                "journey": {
                    "origin": {
                        "id": "string",
                        "name": "string",
                        "type": "string",
                        "address": "string",
                        "city_name": "string",
                        "country_code": "string",
                        "iata_code": "string",
                        "latitude": "number",
                        "longitude": "number"
                    },
                    "destination": {
                        "id": "string",
                        "name": "string",
                        "type": "string",
                        "address": "string",
                        "city_name": "string",
                        "country_code": "string",
                        "iata_code": "string",
                        "latitude": "number",
                        "longitude": "number"
                    },
                    "pickup_at": "string",
                    "pickup_timezone": "string",
                    "return_pickup_at": "string",
                    "duration_minutes": 0,
                    "distance_km": 0
                },
                "vehicle": {
                    "class": "string",
                    "description": {},
                    "max_passengers": 0,
                    "max_bags": 0,
                    "inclusions": [
                        "string"
                    ]
                },
                "total": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "currency": "string",
                "payment_policy": "string",
                "collected_by_driver": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "payable_now": {
                    "amount": 0,
                    "currency": "string",
                    "decimals": 0,
                    "formatted": "string"
                },
                "cancellation": {
                    "refundable": true,
                    "free_until": "string",
                    "penalty": {
                        "amount": "integer",
                        "currency": "string",
                        "decimals": "integer",
                        "formatted": "string"
                    },
                    "description": "string"
                },
                "expires_at": "string"
            }
        ]
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "search_id": "string",
        "count": 0
    }
}
```

### GET /transfers/locations

Places we can be driven to

Transfers are quoted between identifiers from an operator's catalogue, not between addresses — so without this endpoint a partner has nothing valid to put in a search. It is part of the product rather than a convenience.

Query: `country_code`

Response shape (values are placeholders):

```json
{
    "data": {
        "locations": [
            {}
        ]
    },
    "meta": {
        "request_id": "string",
        "timestamp": "string",
        "environment": "string",
        "count": 0
    }
}
```

## Errors

| Code | HTTP | Meaning | Retry |
|---|---|---|---|
| `TB-CORE-1001` | 400 | We could not read that request | no |
| `TB-CORE-1002` | 422 | Some details need correcting | no |
| `TB-CORE-1003` | 404 | Not found | no |
| `TB-CORE-1004` | 403 | Not allowed | no |
| `TB-CORE-1005` | 429 | Too many requests | yes |
| `TB-CORE-1007` | 426 | Please update the app | no |
| `TB-CORE-1008` | 400 | Missing request identifier | no |
| `TB-CORE-1009` | 409 | This request was already submitted | no |
| `TB-CORE-1010` | 409 | Still working on that | yes |
| `TB-CORE-1011` | 400 | Currency not available | no |
| `TB-CORE-1012` | 400 | Language not available | no |
| `TB-CORE-1013` | 405 | Not supported | no |
| `TB-AUTH-1102` | 401 | Your session has expired | no |
| `TB-AUTH-1105` | 403 | This account is suspended | no |
| `TB-AUTH-1106` | 400 | That code is incorrect | yes |
| `TB-AUTH-1107` | 410 | That code has expired | no |
| `TB-AUTH-1108` | 429 | Too many codes requested | yes |
| `TB-AUTH-1114` | 401 | Security check failed | no |
| `TB-SRCH-1301` | 422 | Choose a departure date in the future | no |
| `TB-SRCH-1302` | 422 | Check your return date | no |
| `TB-SRCH-1303` | 422 | Choose two different places | no |
| `TB-SRCH-1306` | 422 | Too many passengers | no |
| `TB-SRCH-1307` | 422 | Each infant needs an adult | no |
| `TB-FLIGHT-1401` | 410 | This fare is no longer available | no |
| `TB-FLIGHT-1402` | 410 | This offer has expired | no |
| `TB-FLIGHT-1403` | 409 | The price for this flight has changed | no |
| `TB-FLIGHT-1406` | 422 | Please confirm the price first | no |
| `TB-HOTEL-1501` | 410 | This room is no longer available | no |
| `TB-HOTEL-1502` | 409 | The rate has changed | no |
| `TB-HOTEL-1503` | 409 | This property cannot confirm the booking | no |
| `TB-HOTEL-1504` | 422 | Check your dates | no |
| `TB-HOTEL-1505` | 422 | Too many guests for these rooms | no |
| `TB-HOTEL-1507` | 422 | The room needs a name | no |
| `TB-PARTNER-3001` | 402 | Insufficient balance | no |
| `TB-PARTNER-3002` | 403 | Account suspended | no |
| `TB-PARTNER-3003` | 409 | This top-up has already been processed | no |
| `TB-PARTNER-3004` | 401 | Invalid API credentials | no |
| `TB-PARTNER-3005` | 403 | This key is not permitted to do that | no |
| `TB-PARTNER-3006` | 429 | Rate limit exceeded | yes |
| `TB-PARTNER-3007` | 402 | Monthly quota exhausted | no |
| `TB-PARTNER-3008` | 403 | Product not included in this plan | no |
| `TB-PARTNER-3009` | 403 | Sandbox keys cannot do that | no |
| `TB-PARTNER-3010` | 402 | No balance in this currency | no |
| `TB-PARTNER-3011` | 422 | This API settles in USD | no |
| `TB-PARTNER-3012` | 403 | This service is not active on your account | no |
| `TB-PARTNER-3013` | 429 | Daily limit reached | yes |
| `TB-PARTNER-3014` | 422 | This order exceeds your per-booking limit | no |
| `TB-PARTNER-3015` | 409 | No permitted supplier can fulfil this | no |
| `TB-TRANSFER-1601` | 410 | This transfer is no longer available | no |
| `TB-TRANSFER-1602` | 422 | Too many passengers or bags | no |
| `TB-BOOK-1701` | 410 | Your booking hold has expired | no |
| `TB-BOOK-1704` | 409 | This booking cannot be changed right now | no |
| `TB-BOOK-1706` | 422 | This fare cannot be cancelled | no |
| `TB-BOOK-1708` | 202 | Your booking needs a quick review | no |
| `TB-BOOK-1709` | 409 | This booking is being updated | yes |
| `TB-BOOK-1712` | 409 | A booking needs at least one item | no |
| `TB-BOOK-1713` | 409 | This booking can no longer be changed | no |
| `TB-BOOK-1714` | 409 | That price is in a different currency | no |
| `TB-BOOK-1716` | 409 | This booking needs a human to issue the ticket | no |
| `TB-BOOK-1715` | 409 | The supplier could not confirm this order | no |
| `TB-PAX-1801` | 422 | Name must match the travel document | no |
| `TB-PAX-1802` | 422 | Use Latin characters for names | no |
| `TB-PAX-1803` | 422 | This passport expires before travel | no |
| `TB-PAX-1804` | 422 | Passport expires soon after travel | no |
| `TB-PAX-1805` | 422 | Date of birth does not match the passenger type | no |
| `TB-PAX-1806` | 422 | Check the infant age | no |
| `TB-PAX-1807` | 422 | Passenger count does not match the fare | no |
| `TB-PAX-1808` | 422 | Check the document number | no |
| `TB-PAX-1809` | 422 | Contact details needed | no |
| `TB-PROV-3001` | 503 | This airline's system is unavailable | yes |
| `TB-PROV-3002` | 504 | The airline is taking longer than usual | yes |
| `TB-PROV-3003` | 502 | Something went wrong on our side | yes |
| `TB-PROV-3004` | 429 | We're very busy right now | yes |
| `TB-PROV-3005` | 500 | Something went wrong on our side | no |
| `TB-PROV-3006` | 409 | The airline did not accept these details | no |
| `TB-PROV-3007` | 500 | Something went wrong on our side | no |
| `TB-PROV-3008` | 503 | This option is temporarily unavailable | yes |
| `TB-SYS-5001` | 500 | Something went wrong on our side | yes |
