Quotes and booking
The flow
POST /quotes— describe the trip, receive offers (one per vehicle type).- Show the offers to your customer. Each offer is valid for 30 minutes (
expires_at). POST /bookings— send the chosenoffer_idwith passenger details.- Store our
referenceand follow the booking through webhooks orGET /bookings/{reference}.
The price in the offer is the price of the booking: offers are signed, so the price cannot change between quote and booking.
Places
from and to accept one of three forms:
| Form | Example | Use for |
|---|---|---|
iata | { "iata": "IST" } | Airports. |
location_id | { "location_id": 412 } | Places from GET /locations: mainly airports, plus the ports, stations and hotels we have registered. |
lat + lng + address | { "lat": 41.0369, "lng": 28.9850, "address": "Taksim Square, Istanbul", "country": "TR" } | Any address. Add country (ISO 3166-1 alpha-2) when you know it. |
Prefer iata for airports. For hotels and addresses, send lat + lng + address from your own geocoder (Google Places or similar): prices come from our service zones, so any point inside a zone is priced like a registered hotel. GET /locations is the same list in test and live and is not a hotel directory; an empty result for a hotel name is normal.
Pick-up time from a flight
Passengers often know their flight number but not the right pick-up time. POST /flights/pickup-time looks up the flight and suggests it:
POST /flights/pickup-time
{
"flight_number": "TK1937",
"date": "2026-11-20",
"from": { "lat": 41.0369, "lng": 28.9850, "address": "Taksim Square, Istanbul" },
"to": { "iata": "IST" }
}{
"data": {
"flight_number": "TK1937", "airline": "Turkish Airlines",
"departure": { "airport": "IST", "scheduled_at": "2026-11-20T07:40", "terminal": "1" },
"arrival": { "airport": "BRU", "scheduled_at": "2026-11-20T10:10", "terminal": null },
"domestic": false, "trip": "to_airport", "airport_match": true,
"airport_before_minutes": 180, "drive_minutes": 50, "rush_minutes": 0,
"suggested_pickup_at": "2026-11-20T03:50"
}
}- To the airport (
trip: to_airport): the latest pick-up so the passenger is at the airport 3 hours before an international or 1.5 hours before a domestic departure, including driving time and a rush-hour margin. Early flights can give a time on the previous day. - From the airport (
trip: from_airport): the landing time. - All times are local.
airport_match: falsemeans the flight does not use the airport of this trip; check the flight number. suggested_pickup_atisnullwhen the flight was found but no pick-up can be suggested: the flight does not use the airport of this trip (airport_match: false) or the trip neither starts nor ends at an airport (trip: null).- Errors:
404 flight_not_found(no such flight on that date),503 flight_data_unavailable. It is a helper only: use the suggested time in yourPOST /quotesrequest.
Transfer quote
POST /quotes
{
"service": "transfer",
"from": { "iata": "AYT" },
"to": { "address": "Rixos Premium Belek", "lat": 36.8571, "lng": 31.0418, "country": "TR" },
"date": "2026-11-20",
"time": "14:30",
"passengers": { "adults": 2, "children": 1, "infants": 0 },
"luggage": 3
}dateandtimeare the local pick-up time. For airport pick-ups, send the scheduled landing time; the driver tracks the flight.passengers.adultsis 1–99. Children and infants count towards vehicle capacity.- Dates can be from today up to 400 days ahead.
{
"data": {
"service": "transfer",
"currency": "EUR",
"distance_km": 34.8,
"timezone": "Europe/Istanbul",
"out_of_service_area": false,
"offers": [
{
"offer_id": "of_eyJ2Ijo…",
"vehicle_type": { "id": 2, "code": "MINIVAN", "name": "Minivan", "max_passengers": 6, "max_luggage": 6 },
"price": 54.0,
"currency": "EUR",
"confirmation": "instant",
"free_cancellation_hours": 24,
"expires_at": "2026-11-01T09:30:00+00:00"
}
]
}
}| Field | Meaning |
|---|---|
price | Final price for the whole vehicle, VAT included, as a JSON number (it may come as 54 or 54.5; parse it as a decimal). null = price on request. |
confirmation | instant: the booking is confirmed immediately. on_request: we confirm it manually (see Booking lifecycle). |
free_cancellation_hours | Free cancellation until this many hours before pick-up; later cancellations may be charged. |
out_of_service_area | true when the address is outside our regular service areas. You can still book on request. |
Offers are sorted by price. An empty offers list means we cannot serve the trip — please let us know if you expected results.
Hourly chauffeur quote
POST /quotes
{ "service": "hourly", "from": { "iata": "IST" }, "date": "2026-11-20", "time": "09:00", "hours": 4, "passengers": { "adults": 2 } }hours is 1–48. to is not used.
Booking
POST /bookings
Idempotency-Key: order-10045
{
"items": [
{ "offer_id": "of_eyJ2Ijo…", "flight_number": "TK2412", "note": "Child seat for a 3-year-old" }
],
"lead_passenger": { "name": "Anna Peeters", "phone": "+32 470 12 34 56", "email": "anna@example.com" },
"passengers": [ { "name": "Tom Peeters", "type": "adult" }, { "name": "Lise Peeters", "type": "child" } ],
"reference": "ORDER-10045",
"note": "VIP customer"
}- Up to 10
items(transfers / hourly) and 10services(airport services) in one booking — for example an arrival and a return transfer. lead_passenger.nameis required. A mobile phone number lets the driver reach the traveller; please always send it.flight_number(for exampleTK2412) is strongly recommended for airport pick-ups.referenceis your own reference (max 80 characters). In booking responses it comes back ascustomer_reference(our own reference isreference, e.g.PXT-01234). You can search by it withGET /bookings?reference=.GET /bookingslists all bookings of your company account: those made through the API with any of your keys, and those made on the customer portal or by our operations team for your company.- Always send an
Idempotency-Key; see Errors and limits.
The response (201 Created) is the booking:
{
"data": {
"reference": "PXT-01234",
"status": "confirmed",
"customer_reference": "ORDER-10045",
"currency": "EUR",
"total": 54.0,
"paid": 0.0,
"payment_url": null,
"lead_passenger": { "name": "Anna Peeters", "phone": "+32 470 12 34 56", "email": "anna@example.com" },
"items": [
{
"id": 1, "type": "transfer", "status": "scheduled",
"pickup_at": "2026-11-20T14:30", "timezone": "Europe/Istanbul",
"from": { "iata": "AYT", "name": "Antalya Airport" },
"to": { "name": "Rixos Premium Belek", "lat": 36.8571, "lng": 31.0418 },
"vehicle_type": { "id": 2, "code": "MINIVAN", "name": "Minivan" },
"passengers": { "adults": 2, "children": 1, "infants": 0 },
"luggage": 3, "flight_number": "TK2412", "price": 54.0, "driver": null
}
],
"cancellation": { "fee_if_cancelled_now": 0.0 },
"created_at": "2026-11-01T09:05:12+00:00"
}
}Offer errors
| HTTP | error.code | What to do |
|---|---|---|
| 410 | offer_expired | The offer is older than 30 minutes. Quote again. |
| 422 | invalid_offer | The offer_id is damaged, belongs to another account or another environment (test vs live). |
Instant confirmation only (B2C)
Selling to travellers (B2C) usually means you cannot show "pending confirmation". Two ways to make sure every booking is confirmed on the spot:
- Per request: send
"instant_only": trueinPOST /quotesand inPOST /bookings. The quote then returns only offers withconfirmation: instant(and"instant_only": truein the response). - For the whole account: ask us to set your account to instant confirmation only;
instant_onlyis then always on, andon_request/offeredbookings never occur for you.
POST /bookings
{ "items": [{ "offer_id": "of_…" }], "lead_passenger": { "name": "Jane Smith" }, "instant_only": true }If the booking cannot be confirmed instantly, nothing is created and you get 409 not_instant with error.details.reason:
reason | Meaning | Tell the traveller |
|---|---|---|
no_price | No instant price for this trip. | Not available online; offer another vehicle. |
short_notice | The pick-up is too soon to confirm instantly. | Too close to departure; choose a later time. |
credit_limit | Your account credit limit would be exceeded. | Not available right now (contact us). |
Card-paying accounts are checked before any payment is taken, so a traveller never pays for a booking that is not confirmed.
Card payments
If your account pays by card per booking, a new booking has status pending_payment and a payment_url. Send the traveller (or your own checkout) to that secure page. When the payment succeeds, the booking becomes confirmed and you receive a booking.updated webhook. Unpaid bookings are not fulfilled.
Accounts paying by monthly invoice never see pending_payment.
In the sandbox, payment_url opens a test payment page: click Complete payment (test) and the booking becomes confirmed exactly as in live mode (webhook included). Alternatively call POST /test/bookings/{reference}/simulate with {"event": "payment_completed"}.
Cancelling
curl -X POST https://paxvelhub.com/api/v1/bookings/PXT-01234/cancel \
-H "Authorization: Bearer $PAXVEL_KEY" -H "Content-Type: application/json" \
-d '{ "reason": "Traveller cancelled the trip" }'The whole booking is cancelled. Items cancelled inside the free-cancellation window cost nothing; later cancellations are charged according to your cancellation policy, shown in cancellation_fee per item and in total. Before cancelling you can read the expected fee in cancellation.fee_if_cancelled_now of GET /bookings/{reference}.
To change a booking (time, address, passengers), cancel and book again while free cancellation applies, or ask us under Questions for later changes.