Paxvel Developers
DocsAPI referenceConsole Sign in Request access

Quotes and booking

The flow

  1. POST /quotes — describe the trip, receive offers (one per vehicle type).
  2. Show the offers to your customer. Each offer is valid for 30 minutes (expires_at).
  3. POST /bookings — send the chosen offer_id with passenger details.
  4. Store our reference and follow the booking through webhooks or GET /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:

FormExampleUse 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"
  }
}

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
}
{
  "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"
      }
    ]
  }
}
FieldMeaning
priceFinal 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.
confirmationinstant: the booking is confirmed immediately. on_request: we confirm it manually (see Booking lifecycle).
free_cancellation_hoursFree cancellation until this many hours before pick-up; later cancellations may be charged.
out_of_service_areatrue 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"
}

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

HTTPerror.codeWhat to do
410offer_expiredThe offer is older than 30 minutes. Quote again.
422invalid_offerThe 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:

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:

reasonMeaningTell the traveller
no_priceNo instant price for this trip.Not available online; offer another vehicle.
short_noticeThe pick-up is too soon to confirm instantly.Too close to departure; choose a later time.
credit_limitYour 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.