# Development quickstart

Learn how to search and book a flight through the CitizenPlane (opens new window) v4 API.


The CitizenPlane v4 API provides a standardized interface for distributors to search, price, order and book flights. The API follows an NDC-inspired booking flow. CitizenPlane (opens new window) only supports EUR as its currency.


# Testing

You'll be given a sandbox API key for the implementation phase & testing process. You can search & book test flights using this sandbox key. No real charge will be induced.


# Complete flow

The search-to-booking flow with the v4 API is as follows:

  1. Search Offers
  2. Get Offer
  3. Get Baggage Options
  4. Create Order
  5. Confirm Order
  6. Get Reservation

# Step 1: Search Offers

Search for available flight offers matching the traveler's criteria. You can search for one-way or round-trip itineraries using either airport codes or city codes.

Flight content

Please note that all our flights are non-refundable, non-exchangeable and have no flex fares. Only ECONOMY class is currently supported.

# Implementation

curl 'https://booking-api.citizenplane.com/v4/search' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}' \
-d '{
  "adults": 1,
  "children": 0,
  "infants": 0,
  "tripType": "ONE_WAY",
  "travelClass": "ECONOMY",
  "outboundSegment": {
    "origin": { "code": "CDG", "type": "AIRPORT" },
    "destination": { "code": "AYT", "type": "AIRPORT" },
    "departureDate": "2025-08-06"
  },
  "inboundSegment": null
}'

# Example Response

{
  "offers": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "outboundSegment": {
        "sections": [
          {
            "origin": "CDG",
            "destination": "AYT",
            "departureDate": "2025-08-06T17:45:00",
            "arrivalDate": "2025-08-06T22:40:00",
            "travelClass": "ECONOMY",
            "availableSeats": 10,
            "flightNumber": "511",
            "operatingCarrierCode": "XQ",
            "marketingCarrierCode": "XQ",
            "technicalStops": []
          }
        ]
      },
      "fare": {
        "passengerPricingByPassengerType": {
          "ADULT": {
            "fareAmount": { "amount": 627.27, "currency": "EUR" },
            "taxAmount": { "amount": 0, "currency": "EUR" },
            "numberOfPassengers": 1
          }
        },
        "includedBaggage": {
          "checkedBaggageQuantity": 1,
          "cabinBaggageQuantity": 1
        },
        "price": {
          "totalAmount": 627.27,
          "sourceAmount": 627.27,
          "currency": "EUR"
        }
      },
      "paymentOptions": [
        { "type": "cash", "fee": { "amount": 0, "currency": "EUR" } }
      ],
      "connectivityStandard": "NDC"
    }
  ]
}

The response contains an array of offers, each with flight segments, pricing, and included baggage information.

Go to the API reference


# Step 2: Get Offer

Once you've identified an interesting offer from the search results, retrieve the detailed offer by providing the exact flight sections. This step verifies availability and pricing with the supplier.

The returned offer includes:

  • Detailed fare information with per-passenger pricing
  • Included baggage details
  • Additional passenger information requirements (e.g. passport needed)
  • An offer ID valid for 30 minutes

# Implementation

curl 'https://booking-api.citizenplane.com/v4/offer' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}' \
-d '{
  "adults": 1,
  "children": 0,
  "infants": 0,
  "tripType": "ONE_WAY",
  "isResidentDiscountEligible": false,
  "outboundSegment": {
    "sections": [
      {
        "origin": "CDG",
        "destination": "AYT",
        "departureDate": "2025-08-06T17:45:00",
        "arrivalDate": "2025-08-06T22:40:00",
        "flightNumber": "511",
        "operatingCarrierCode": "XQ",
        "marketingCarrierCode": "XQ"
      }
    ]
  },
  "inboundSegment": null
}'

# Example Response

{
  "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "outboundSegment": {
    "sections": [
      {
        "origin": "CDG",
        "destination": "AYT",
        "departureDate": "2025-08-06T17:45:00",
        "arrivalDate": "2025-08-06T22:40:00",
        "travelClass": "ECONOMY",
        "flightNumber": "511",
        "departureTerminal": "1",
        "arrivalTerminal": "1",
        "operatingCarrierCode": "XQ",
        "marketingCarrierCode": "XQ",
        "technicalStops": []
      }
    ]
  },
  "cheapestFare": {
    "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "passengerPricingByPassengerType": {
      "ADULT": {
        "fareAmount": { "amount": 627.27, "currency": "EUR" },
        "taxAmount": { "amount": 0, "currency": "EUR" },
        "numberOfPassengers": 1
      }
    },
    "includedBaggage": [
      { "quantity": 1, "descriptor": { "weight": 20 }, "type": "CHECK_IN" },
      { "quantity": 1, "descriptor": { "weight": 10, "dimensions": { "width": 56, "height": 45, "depth": 25 } }, "type": "CABIN" }
    ],
    "totalPrice": { "amount": 627.27, "currency": "EUR" }
  },
  "providerAdditionalPassengerInformationByPassengerType": {
    "ADULT": {
      "providerIdentificationTypeOptions": ["PASSPORT"],
      "providerRequiredAdditionalFields": ["BIRTH_DATE"]
    }
  },
  "paymentOptions": [
    { "type": "cash", "fee": { "amount": 0, "currency": "EUR" } }
  ],
  "connectivityStandard": "NDC",
  "validatingCarrier": "XQ"
}

Offer TTL

The offer is cached for 30 minutes. After that, you must retrieve a new offer.

Go to the API reference


# Step 3: Get Baggage Options

Retrieve the available paid baggage options (checked and cabin) for the offer. This returns only the paid options — free baggage is already included in the offer's includedBaggage field.

# Implementation

curl 'https://booking-api.citizenplane.com/v4/offer/{offerId}/baggage' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}'

# Example Response

{
  "fareBaggageOptions": [
    {
      "fareId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
      "itineraryOptions": [],
      "outboundSegmentOptions": [
        {
          "type": "CHECK_IN",
          "maxPiecesPerPassenger": 1,
          "maxKilosPerPiece": 20,
          "pricePerPiece": { "amount": 36.05, "currency": "EUR" }
        },
        {
          "type": "CABIN",
          "maxPiecesPerPassenger": 1,
          "maxKilosPerPiece": 10,
          "pricePerPiece": { "amount": 15.00, "currency": "EUR" }
        }
      ],
      "inboundSegmentOptions": null
    }
  ]
}

Go to the API reference


# Step 4: Create Order

Create an order with passenger details, selected baggage, and customer contact information. The offer ID and fare ID must be provided as query parameters.

# Implementation

curl 'https://booking-api.citizenplane.com/v4/order?offerId={offerId}&fareId={fareId}' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}' \
-d '{
  "passengers": [
    {
      "number": 1,
      "type": "ADULT",
      "title": "MR",
      "name": "John",
      "firstLastName": "Doe",
      "gender": "MALE",
      "birthDate": "1990-01-15",
      "nationalityCountryCode": "FR",
      "identification": {
        "type": "PASSPORT",
        "identificationNumber": "ABCDE123",
        "expirationDate": "2030-01-01",
        "issueCountryCode": "FR"
      },
      "phone": { "number": "+33600000000", "countryCode": "FR" }
    }
  ],
  "selectedBaggage": [
    {
      "passengerNumber": 1,
      "itineraryBaggageSelection": [],
      "outboundSegmentBaggageSelection": [
        { "type": "CHECK_IN", "numberOfPieces": 1, "kilosPerPiece": 20 }
      ],
      "inboundSegmentBaggageSelection": null
    }
  ],
  "selectedSeats": [],
  "customerContact": {
    "name": "John",
    "lastNames": "Doe",
    "email": "john.doe@example.com",
    "countryCode": "FR",
    "phone": { "number": "+33600000000", "countryCode": "FR" },
    "address": "123 Rue de Paris",
    "cityName": "Paris",
    "zipCode": "75001"
  },
  "paymentMethod": { "cash": true }
}'

# Example Response

{
  "id": "d4e5f6a7-b8c9-0123-defg-234567890123",
  "offer": {
    "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "outboundSegment": {
      "sections": [
        {
          "origin": "CDG",
          "destination": "AYT",
          "departureDate": "2025-08-06T17:45:00",
          "arrivalDate": "2025-08-06T22:40:00",
          "travelClass": "ECONOMY",
          "flightNumber": "511",
          "operatingCarrierCode": "XQ",
          "marketingCarrierCode": "XQ"
        }
      ]
    },
    "fare": {
      "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
      "passengerPricingByPassengerType": {
        "ADULT": {
          "fareAmount": { "amount": 627.27, "currency": "EUR" },
          "taxAmount": { "amount": 0, "currency": "EUR" },
          "numberOfPassengers": 1
        }
      },
      "totalPrice": { "amount": 627.27, "currency": "EUR" }
    },
    "connectivityStandard": "NDC"
  },
  "passengers": [
    {
      "number": 1,
      "type": "ADULT",
      "title": "MR",
      "name": "John",
      "firstLastName": "Doe",
      "gender": "MALE",
      "birthDate": "1990-01-15",
      "nationalityCountryCode": "FR"
    }
  ],
  "selectedBaggage": [
    {
      "passengerNumber": 1,
      "outboundSegmentBaggageSelection": [
        {
          "type": "CHECK_IN",
          "numberOfPieces": 1,
          "kilosPerPiece": 20,
          "pricePerPiece": { "amount": 36.05, "currency": "EUR" }
        }
      ]
    }
  ],
  "status": "OPEN",
  "totalPrice": { "amount": 663.32, "currency": "EUR" }
}

The response returns an order with status OPEN and a unique order ID.

Go to the API reference


# Step 5: Confirm Order

Confirm the order to trigger the actual booking. Once confirmed, a reservation ID is generated and the booking is processed.

WARNING

An order can only be confirmed once. Duplicate confirmation requests will be rejected.

# Implementation

curl -X POST 'https://booking-api.citizenplane.com/v4/order/{orderId}/confirm' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}'

# Example Response

{
  "id": "d4e5f6a7-b8c9-0123-defg-234567890123",
  "offer": {
    "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "outboundSegment": { "sections": [...] },
    "fare": { ... },
    "connectivityStandard": "NDC"
  },
  "passengers": [...],
  "selectedBaggage": [...],
  "selectedSeats": [],
  "customerContact": { ... },
  "status": "CONFIRMING",
  "reservationId": "e5f6a7b8-c9d0-1234-efgh-345678901234",
  "totalPrice": { "amount": 663.32, "currency": "EUR" }
}

The response returns the order with status CONFIRMING and a reservationId.

Go to the API reference


# Step 6: Get Reservation

Retrieve the final reservation details including the PNR. You can retrieve the reservation either by reservation ID or by order ID.

# By reservation ID

curl 'https://booking-api.citizenplane.com/v4/reservation/{reservationId}' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}'

# By order ID

curl 'https://booking-api.citizenplane.com/v4/reservation?orderId={orderId}' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}'

# Example Response

{
  "id": "e5f6a7b8-c9d0-1234-efgh-345678901234",
  "orderId": "d4e5f6a7-b8c9-0123-defg-234567890123",
  "pnr": "CPABCD",
  "email": "john.doe@example.com",
  "outboundSegment": {
    "sections": [
      {
        "origin": "CDG",
        "destination": "AYT",
        "departureDate": "2025-08-06T17:45:00",
        "arrivalDate": "2025-08-06T22:40:00",
        "travelClass": "ECONOMY",
        "flightNumber": "511",
        "departureTerminal": "1",
        "arrivalTerminal": "1",
        "operatingCarrierCode": "XQ",
        "marketingCarrierCode": "XQ",
        "technicalStops": []
      }
    ]
  },
  "passengers": [
    {
      "number": 1,
      "type": "ADULT",
      "title": "MR",
      "name": "John",
      "firstLastName": "Doe",
      "gender": "MALE",
      "birthDate": "1990-01-15",
      "nationalityCountryCode": "FR"
    }
  ],
  "selectedBaggage": [...],
  "selectedSeats": [],
  "customerContact": { ... },
  "fare": { ... }
}

The response includes the PNR, passenger details, flight segments, baggage selections, and fare information.

Go to the API reference


# Error Responses

All endpoints return errors in a consistent format with a message and type field. Here are common error scenarios you may encounter:

Expired offer (400):

{
  "message": "The requested offer is no longer available or has expired",
  "type": "OFFER_NOT_AVAILABLE"
}

Invalid request parameters (400):

{
  "message": "The request payload is invalid or missing required fields",
  "type": "INVALID_REQUEST_PARAMETERS"
}

Order not found (404):

{
  "message": "The specified order could not be found",
  "type": "ORDER_NOT_FOUND"
}

Authentication error (401):

{
  "message": "Authentication failed",
  "type": "AUTHENTICATION_ERROR"
}

Go to the API reference — Errors for the full list of error types.