# API Reference

The CitizenPlane API is based on REST (opens new window), returns JSON responses and uses standard HTTP codes. The API base url is https://booking-api.citizenplane.com.

To get started with CitizenPlane API, send us a mail. We'll create your account, and send the credentials to start using our API.

URL-based versioning is supported in our API. Each version will be appended to the base url.

# Authentication

The CitizenPlane API uses Bearer Tokens to authenticate requests. All requests made whithout authentication will fail, as well as requests made over plain HTTP rather than HTTPS.

To get your token, send us a mail and we'll assign one for you.

# Errors

CitizenPlane uses conventional HTTP response codes to indicate the success or failure of an API request. Codes in the 4xx range indicate a request that failed given the information provided (e.g., a required parameter was omitted, a request has expired, a charge failed, etc.). Codes in the 5xx range indicate an internal error, which is rare.

# HTTP Status Codes Summary

Code Status Meaning
400 Bad Request Your request is invalid (e.g payload sent doesn't match request signature)
401 Unauthorized Your API key is wrong
403 Forbidden The requested action cannot be permitted (e.g process a booking on a ABORTED request)
404 Not Found The specified data could not be found (e.g flight_id doesn't exist)
409 Conflict The request wasn't processed. Potential reasons are: flight is no longer available or found in our cache, unacceptable price difference between request & booking, flight has already been booked, etc.
422 Unprocessable Entity A validation error has occured (these are payment-related errors)
500 Internal Server Error Error on CitizenPlane's end. Try again later and contact us if the issue remain

# Test mode

Test mode tokens will be provided during your integration period. These tokens will allow you to search for flights and process bookings on a set of demo flights with no danger of spending any money or booking flights you don't want.

Once you're done with the integration, we will send you production tokens, allowing you to access our actual stock and process live payments and bookings.

# Key concepts

The CitizenPlane API is very simple to use and implement. In only 4 steps, you can complete an entire booking flow, from searching a flight to processing the booking. You'll only need to understand a few concepts in order to get started with the CitizenPlane API.

# Booking requests

In order to process a booking, a booking request must be created first. Creating a booking request allows you to have the most up-to-date information regarding price & availability. You'll only need one or several request ids (up to three) to confirm and process the booking.

The request will expire after 3 minutes and any booking using an id for an expired request will fail.

Check our development quickstart to get an overview of the complete booking flow.

# Customers

Customers in the CitizenPlane API are the entity (individual or business) whose card is processed during the booking. Customers can be identified with either:

  • A unique customer_code, sent at every step of the booking process. Customer codes are used to identify OTAs within CitizenPlane's system (e.g govoyagesfr for GoVoyages.fr (opens new window), kiwicom for Kiwi.com (opens new window)). If you want to know to list of supported OTAs or add your OTA and get a customer code, please contact us.
  • A custom customer_data object sent with https://booking-api.citizenplane.com/v2/bookings payload containing all required information for invoicing.

# Exporting our content

If you need an exhaustive list of all the content we provide, you can ask tech@citizenplane.com to activate a drop of a CSV file on our ftp server.

We offer two specific formats: routes or flights. Formats specification will be detailed below. We recommend that you use the routes format, which is a lighter version of our complete export.

# Files structure

Depending on distribution agreements, several files can be dropped onto your FTP folder. You can have as many exports as you have customers with specific content distribution rules. For every other customers, a file containing all the content will also be available. If no customers have specific distribution rules, you will only get the "all" content file.

Customers

Customers are identified by a customer code, sent during the flight search and subsequent steps (request, booking).

All files are appended with the API version currently in use.

TIP

To avoid any confusion, we've also kept the legacy file names: cp_routes.csv & cp_flights.csv. These legacy files will stay for a couple months to allow you to complete the migration to the new file naming structure.

Example routes export structure:

cp_routes_customerOne_v2.csv
cp_routes_customerTwo_v2.csv
cp_routes_all_v2.csv
cp_routes.csv // legacy name

Example flights export structure:

cp_flights_customerOne_v2.csv
cp_flights_customerTwo_v2.csv
cp_flights_all_v2.csv
cp_flights.csv // legacy name

# Routes format

departureAirport;arrivalAirport;departureDate
BSL;PRN;21.06.2022
MLH;PRN;21.06.2022
AAL;AYT;22.06.2022
ACE;CGN;22.06.2022
ADB;AMS;22.06.2022
ADB;CGN;22.06.2022
...

# Flights format

departureAirport;arrivalAirport;departureDate;marketingAirlineCode;marketingFlightNumber;operatingAirlineCode;operatingFlightNumber;bookingClass;classOfService;availableSeats;fareType;baseFareAmountAdult;supplementSumAmountAdult;totalFareAmountAdult;baseFareAmountChild;supplementSumAmountChild;totalFareAmountChild;baseFareAmountInfant;supplementSumAmountInfant;totalFareAmountInfant;currency;journeyDuration;overNight;stops;segmentFirstDepartureAirport;segmentFirstArrivalAirport;segmentFirstMarketingAirlineCode;segmentFirstMarketingFlightNumber;segmentFirstOperatingAirlineCode;segmentFirstOperatingFlightNumber;segmentFirstDepartureTime;segmentFirstArrivalTime;segmentFirstBookingClass;segmentFirstClassofService;segmentSecondDepartureAirport;segmentSecondArrivalAirport;segmentSecondMarketingAirlineCode;segmentSecondMarketingFlightNumber;segmentSecondOperatingAirlineCode;segmentSecondOperatingFlightNumber;segmentSecondDepartureTime;segmentSecondArrivalTime;segmentSecondBookingClass;segmentSecondClassofService;segmentThirdDepartureAirport;segmentThirdArrivalAirport;segmentThirdMarketingAirlineCode;segmentThirdMarketingFlightNumber;segmentThirdOperatingAirlineCode;segmentThirdOperatingFlightNumber;segmentThirdDepartureTime;segmentThirdArrivalTime;segmentThirdBookingClass;segmentThirdClassofService;baggageFirstWeight;baggageFirstAmount;baggageSecondWeight;baggageSecondAmount;baggageThirdWeight;baggageThirdAmount;checkInFee;airportCheckInFee;creditCardFee;combinationGroup;flightKey;tariffClass
NAS;FLL;19.11.2022;WU;102;WU;102;Y;Y;*;OW;;;243.08;;;243.08;;;63.35;EUR;;0;0;NAS;FLL;WU;102;WU;102;11:00;11:50;Y;Y;;;;;;;;;;;;;;;;;;;;;18;0.00;;;;;;;0.03;;;
PRN;DUS;19.11.2022;IV;620;IV;620;Y;Y;*;OW;;;324.45;;;324.45;;;85.90;EUR;;0;0;PRN;DUS;IV;620;IV;620;11:05;13:30;Y;Y;;;;;;;;;;;;;;;;;;;;;20;0.00;;;;;;;0.03;;;
PRN;STR;19.11.2022;IV;630;IV;630;Y;Y;*;OW;;;224.54;;;224.54;;;58.09;EUR;;0;0;PRN;STR;IV;630;IV;630;11:50;13:50;Y;Y;;;;;;;;;;;;;;;;;;;;;20;0.00;;;;;;;0.03;;;
PRN;MLH;19.11.2022;C3;1806;C3;1806;Y;Y;2;OW;;;115.36;;;115.36;;;30.90;EUR;;0;0;PRN;MLH;C3;1806;C3;1806;13:30;15:40;Y;Y;;;;;;;;;;;;;;;;;;;;;20;0.00;;;;;;;0.03;;;
...

# Endpoints

# Search flights

Use this endpoint to retrieve a list of flights that you can book. Each flight is identified by a unique ID that will be needed to create a request.

GET

https://booking-api.citizenplane.com/v2/flights

# Our flight content

Roundtrips and multiple currencies are not currently supported. All flights can have one, two or three segments and are non-refundable, non-exchangeable with no flex fares.

# Query parameters

Parameter Type Status Description
origin string required Origin airport IATA code (CDG for instance).
destination string required Destination airport IATA code (AYT for instance).
start date optional Date from which the flight search will be operated. Expected format: YYYY-MM-DD. Defaults to current day.
end date optional Date until which the flight search will be operated, up to max +3 days from start. Expected format: YYYY-MM-DD. Defaults to start + 3 days.
seats integer optional Minimum number of seats to search for.
next_page_id string optional Specify the page to retrieve. You get the first nexx_page_id cursor at the end of the first page of results. Each page displays up to 100 results.

Example request

curl "https://booking-api.citizenplane.com/v2/flights?origin=CDG&destination=AYT&start=2022-08-06&seats=1" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer {your_api_token}"

Example response

{
	"results": [
		{
			"id": 4014831,
			"need_apis": true,
			"cabin_class": "economy",
			"booking_class": "Y",
			"included_airport_tax": 59.71,
			"duration": "07:55",
			"luggage_options": [
				{
					"weight": 20,
					"quantity": 1,
					"price": 36.05
				}
			],
			"departure_date": "2022-08-06 13:45:00",
			"arrival_date": "2022-08-06 22:40:00",
			"origin": {
				"iata_code": "CDG",
				"city_name": "Paris",
				"country_name": "France",
				"timezone": "Europe/Paris"
			},
			"destination": {
				"iata_code": "AYT",
				"city_name": "Antalya",
				"country_name": "Turkey",
				"timezone": "Europe/Istanbul"
			},
			"segments": [
				{
					"via_airport": null,
					"origin": {
						"iata_code": "CDG",
						"city_name": "Paris",
						"country_name": "France",
						"timezone": "Europe/Paris"
					},
					"destination": {
						"iata_code": "WAW",
						"city_name": "Warsaw",
						"country_name": "Poland",
						"timezone": "Europe/Warsaw"
					},
					"operating_carrier_flight_number": 1346,
					"operating_carrier": {
						"name": "Air France",
						"iata_code": "AF"
					},
					"marketing_carrier_flight_number": 1346,
					"marketing_carrier": {
						"name": "Air France",
						"iata_code": "AF"
					},
					"departure_date": "2022-08-06 13:45:00",
					"arrival_date": "2022-08-06 16:05:00",
					"duration": "02:20"
				},
				{
					"via_airport": null,
					"origin": {
						"iata_code": "WAW",
						"city_name": "Warsaw",
						"country_name": "Poland",
						"timezone": "Europe/Warsaw"
					},
					"destination": {
						"iata_code": "AYT",
						"city_name": "Antalya",
						"country_name": "Turkey",
						"timezone": "Europe/Istanbul"
					},
					"operating_carrier_flight_number": 421,
					"operating_carrier": {
						"name": "Sunexpress",
						"iata_code": "XQ"
					},
					"marketing_carrier_flight_number": 421,
					"marketing_carrier": {
						"name": "Sunexpress",
						"iata_code": "XQ"
					},
					"departure_date": "2022-08-06 18:40:00",
					"arrival_date": "2022-08-06 22:40:00",
					"duration": "03:00"
				}
			],
			"available_seats": 10,
			"price": 483.07,
			"infant_price": 129.78,
			"cc_fee": 0.03
		},
		{
			"id": 4014832,
			"need_apis": true,
			"cabin_class": "economy",
			"booking_class": "Y",
			"included_airport_tax": 77.53,
			"duration": "03:55",
			"luggage_options": [
				{
					"weight": 20,
					"quantity": 1,
					"price": 0.00
				}
			],
			"departure_date": "2022-08-06 17:45:00",
			"arrival_date": "2022-08-06 22:40:00",
			"origin": {
				"iata_code": "CDG",
				"city_name": "Paris",
				"country_name": "France",
				"timezone": "Europe/Paris"
			},
			"destination": {
				"iata_code": "AYT",
				"city_name": "Antalya",
				"country_name": "Turkey",
				"timezone": "Europe/Istanbul"
			},
			"segments": [
				{
					"via_airport": null,
					"origin": {
						"iata_code": "CDG",
						"city_name": "Paris",
						"country_name": "France",
						"timezone": "Europe/Paris"
					},
					"destination": {
						"iata_code": "AYT",
						"city_name": "Antalya",
						"country_name": "Turkey",
						"timezone": "Europe/Istanbul"
					},
					"operating_carrier_flight_number": 511,
					"operating_carrier": {
						"name": "Sunexpress",
						"iata_code": "XQ"
					},
					"marketing_carrier_flight_number": 511,
					"marketing_carrier": {
						"name": "Sunexpress",
						"iata_code": "XQ"
					},
					"departure_date": "2022-08-06 17:45:00",
					"arrival_date": "2022-08-06 22:40:00",
					"duration": "03:55"
				}
			],
			"available_seats": 10,
			"price": 627.27,
			"infant_price": 169.95,
			"cc_fee": 0.03
		},
		{...},
		{...},
	],
	"next_page_id": null
}

# Create a booking request

This endpoint creates booking requests for up to three flights (identified by their id). You can thus simultaneously create three different requests.

POST

https://booking-api.citizenplane.com/v2/requests

Expired requests

A request only lasts for three minutes. Past that deadline, a new request will have to be created to create a booking.

# Body parameters

Parameter Type Status Description
flights array required An array containing up to three flight_id
customer_code string optional Optional string to identify the customer making this request
passengers object required An object containing the number of adults, children or infants for this request (see details below)

# Passengers object

Parameter Type Status Description
adults integer optional Number of adults to book on this flight
children integer optional Number of children to book on this flight
infants integer optional Number of infants (less than 2 year old) to book on this flight

Example request

curl 'https://booking-api.citizenplane.com/v2/requests' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_token}' \
-d '{
	"flights": [
		4014831,
		3795943
	],
	"customer_code": "edreams",
	"passengers": {
		"adults": 1,
		"children": 0,
		"infants": 0
	}
}'

Example response

{
  "requests": [
    {
      "id": 244401,
      "passengers": {
        "adults": 1,
        "children": 0,
        "infants": 0
      },
      "price": 469,
      "infant_price": null,
      "cc_fee": 0,
      "customer_code": "edreams",
      "created_at": "2022-06-28T00:02:25.317Z",
      "flight": {
        "id": 4014831,
        "need_apis": true,
        "cabin_class": "economy",
        "booking_class": "Y",
        "included_airport_tax": 56.28,
        "duration": "07:55",
        "luggage_options": [
          {
            "weight": 20,
            "quantity": 1,
            "price": 35
          }
        ],
        "departure_date": "2022-08-06 13:45:00",
        "arrival_date": "2022-08-06 22:40:00",
        "origin": {
          "iata_code": "CDG",
          "city_name": "Paris",
          "country_name": "France",
          "timezone": "Europe/Paris"
        },
        "destination": {
          "iata_code": "AYT",
          "city_name": "Antalya",
          "country_name": "Turkey",
          "timezone": "Europe/Istanbul"
        },
        "segments": [
          {
            "via_airport": null,
            "origin": {
              "iata_code": "CDG",
              "city_name": "Paris",
              "country_name": "France",
              "timezone": "Europe/Paris"
            },
            "destination": {
              "iata_code": "WAW",
              "city_name": "Warsaw",
              "country_name": "Poland",
              "timezone": "Europe/Warsaw"
            },
            "operating_carrier_flight_number": 1346,
            "operating_carrier": {
              "name": "Air France",
              "iata_code": "AF"
            },
            "marketing_carrier_flight_number": 1346,
            "marketing_carrier": {
              "name": "Air France",
              "iata_code": "AF"
            },
            "departure_date": "2022-08-06 13:45:00",
            "arrival_date": "2022-08-06 16:05:00"
          },
          {
            "via_airport": null,
            "origin": {
              "iata_code": "WAW",
              "city_name": "Warsaw",
              "country_name": "Poland",
              "timezone": "Europe/Warsaw"
            },
            "destination": {
              "iata_code": "AYT",
              "city_name": "Antalya",
              "country_name": "Turkey",
              "timezone": "Europe/Istanbul"
            },
            "operating_carrier_flight_number": 421,
            "operating_carrier": {
              "name": "Sunexpress",
              "iata_code": "XQ"
            },
            "marketing_carrier_flight_number": 421,
            "marketing_carrier": {
              "name": "Sunexpress",
              "iata_code": "XQ"
            },
            "departure_date": "2022-08-06 18:40:00",
            "arrival_date": "2022-08-06 22:40:00"
          }
        ]
      },
      "customer": {
        "name": "eDreams ODIGEO",
        "email": null,
        "is_company": true,
        "company_codes": ["edreams"],
        "verified": true,
        "phonenumber": null,
        "gender": null,
        "address": null,
        "postal_code": null,
        "city_name": null,
        "country_code": "ES",
        "country_name": "Spain",
        "vat_number": null,
        "registration_number": null,
        "created_at": "2022-01-27T09:56:15.773Z",
        "updated_at": "2022-01-31T10:26:20.424Z"
      }
    },
    {
      "id": 244402,
      "passengers": {
        "adults": 1,
        "children": 0,
        "infants": 0
      },
      "price": 555,
      "infant_price": null,
      "cc_fee": 0,
      "customer_code": "edreams",
      "created_at": "2022-06-28T00:02:25.319Z",
      "flight": {
        "id": 3795943,
        "need_apis": false,
        "cabin_class": "economy",
        "booking_class": "Y",
        "included_airport_tax": 66.6,
        "duration": "04:20",
        "luggage_options": [
          {
            "weight": 20,
            "quantity": 1,
            "price": 0
          }
        ],
        "departure_date": "2022-08-13 13:35:00",
        "arrival_date": "2022-08-13 16:55:00",
        "origin": {
          "iata_code": "AYT",
          "city_name": "Antalya",
          "country_name": "Turkey",
          "timezone": "Europe/Istanbul"
        },
        "destination": {
          "iata_code": "CDG",
          "city_name": "Paris",
          "country_name": "France",
          "timezone": "Europe/Paris"
        },
        "segments": [
          {
            "via_airport": null,
            "origin": {
              "iata_code": "AYT",
              "city_name": "Antalya",
              "country_name": "Turkey",
              "timezone": "Europe/Istanbul"
            },
            "destination": {
              "iata_code": "CDG",
              "city_name": "Paris",
              "country_name": "France",
              "timezone": "Europe/Paris"
            },
            "operating_carrier_flight_number": 510,
            "operating_carrier": {
              "name": "Sunexpress",
              "iata_code": "XQ"
            },
            "marketing_carrier_flight_number": 510,
            "marketing_carrier": {
              "name": "Sunexpress",
              "iata_code": "XQ"
            },
            "departure_date": "2022-08-13 13:35:00",
            "arrival_date": "2022-08-13 16:55:00"
          }
        ]
      },
      "customer": {
        "name": "eDreams ODIGEO",
        "email": null,
        "is_company": true,
        "company_codes": ["edreams"],
        "verified": true,
        "phonenumber": null,
        "gender": null,
        "address": null,
        "postal_code": null,
        "city_name": null,
        "country_code": "ES",
        "country_name": "Spain",
        "vat_number": null,
        "registration_number": null,
        "created_at": "2022-01-27T09:56:15.773Z",
        "updated_at": "2022-01-31T10:26:20.424Z"
      }
    }
  ]
}

# Create a Payment Method

WARNING

This step can be omitted if you are authorized to use the self_psp setting and go directly to create a booking.

This external endpoint from Stripe (opens new window) generates a payment_method which will be used in the next step to process the payment and confirm the booking.

POST

https://api.stripe.com/v1/payment_methods

# Using Stripe as payment provider

CitizenPlane's API uses Stripe (opens new window) to process credit card payments. In order to be PCI-compliant (opens new window), CitizenPlane cannot directly process the customer's credit card data.

The Stripe API also returns JSON-encoded responses and accepts form-encoded requests bodies. Several client libraries are available for an easy integration, as explained in the Stripe API documentation (opens new window)

Failed payment

If the payment fails for any reason, the associated request will be considered as aborted and the process is to be started again. In case of a failed payment, the corresponding Stripe error code (opens new window) will be returned.

# Authorization

To perform a request to the Stripe API, you will need to have a Stripe restricted token (e.g sk_test_123456789abcdedf) which we will give along with your CitizenPlane API credentials.

# Card parameters

Parameter Type Status Description
number string required The card number, as a string without any separators
exp_month number required Two-digit number representing the card's expiration month
exp_year number required Four-digit number representing the card's expiration year
cvc string required Card security code. Highly recommended to always include this value, but it's required only for accounts based in European countries
type string required The type of the PaymentMethod (e.g card). An additional hash is included on the PaymentMethod with a name matching this value. It contains additional information specific to the PaymentMethod type.

Example request

curl https://api.stripe.com/v1/payment_methods \
  -u sk_test_123456789abcdedf: \
  -d card[number]=4242424242424242 \
  -d card[exp_month]=12 \
  -d card[exp_year]=2020 \
  -d card[cvc]=123 \
  -d type=card

Example response

{
  "id": "pm_1Ixu0UCLoBt04Rh8x4xr4E2a",
  "object": "payment_method",
  "billing_details": {
    "address": {
      "city": null,
      "country": null,
      "line1": null,
      "line2": null,
      "postal_code": "42424",
      "state": null
    },
    "email": "jenny@example.com",
    "name": null,
    "phone": "+15555555555"
  },
  "card": {
    "brand": "visa",
    "checks": {
      "address_line1_check": null,
      "address_postal_code_check": null,
      "cvc_check": "pass"
    },
    "country": "US",
    "exp_month": 8,
    "exp_year": 2022,
    "fingerprint": "vsS0JwU3bB3xdpsO",
    "funding": "credit",
    "generated_from": null,
    "last4": "4242",
    "networks": {
      "available": ["visa"],
      "preferred": null
    },
    "three_d_secure_usage": {
      "supported": true
    },
    "wallet": null
  },
  "created": 123456789,
  "customer": null,
  "livemode": false,
  "metadata": {
    "order_id": "123456789"
  },
  "type": "card"
}

Payment method

The payment method id will always be appended with pm_ followed by a random string of numbers and letters.

# Create a booking

This endpoint validates the previously created booking requests and processes the booking as well as the associated payment. Once the booking is created, the customer will receive a confirmation by email along with the ticket(s). Passengers can also check their reservation on our website (opens new window). If the booking is linked to multiple requests (up to three), the same PNR will be issued for each booking, so that the customer can find all the bookings with a unique reference.

POST

https://booking-api.citizenplane.com/v2/bookings

# Body parameters

Parameter Type Status Description
requests integer required An array containing up to three different request_id
external_id string optional The id used on your side to identify the booking. This field is optional, though highly recommended in case we need to retrieve a booking in your system
customer_code string optional A string used to identify the customer making this reservation (must be already added in CitizenPlane's system). If you're making a request with a customer that does not have an identification code (such as an individual), do not add this field but refer to customer_data.
customer_data object optional If the customer does not have an associated customer code, use this field to send information relae to the customer. This information will be used for invoicing. This field is required if customer_code is left empty. See child arguments
first_name string required The booker's first name
last_name string required The booker's last name
gender string required The booker's gender
email string required The booker's email
phonenumber string required The booker's phone number
address string optional The booker's address
city_name string optional The booker's city name
country_code string optional The booker's country code
postal_code string optional The booker's postal code
passengers array required An array containing personal information for each passenger on this booking. See child arguments
luggage object required An object containing the luggage option chosen by each passenger, for each booking request.). See child arguments
self_psp boolean required (if authorized) This let you create a booking directly after request step, without needing payment_method to be specified as it is already handled on your side.
payment_method string required (if not using self_psp) An id obtained by sending credit card information to Stripe's API (opens new window)
three_d_secure object optional (if not using self_psp) 3D Secure results obtained previously with your 3D Secure provider
card_token string deprecated DO NOT USE ANYMORE A token obtained by sending credit card information to Stripe's API (opens new window)

# 3D Secure parameters

For more in depth information regarding 3D Secure results you may have to pass with booking details, please refer to Stripe API Reference (opens new window)

Parameter Type Status Description
version string required The version of 3D Secure that was performed. Possible enum values: 1.0.2, 2.1.0, 2.2.0
electronic_commerce_indicator string required The Electronic Commerce Indicator (ECI) is returned by your 3D Secure provider and indicates what degree of authentication was performed. (If your provider returns an ECI of 07 or 00, please discard it: authentication has failed. If your provider returns an ECI of 06 or 01, attempt was only acknowledged and payment won't be processed by CitizenPlane. If desired, you may make an unauthenticated payment by leaving three_d_secure unset.)
cryptogram string required The cryptogram, also known as the “authentication value” (AAV, CAVV or AEVV). This value is 20 bytes, base64-encoded into a 28-character string. (Most 3D Secure providers will return the base64-encoded version, which is what you should specify here.)
transaction_id boolean required For 3D Secure 1, the XID. For 3D Secure 2, the Directory Server Transaction ID (dsTransID).

# Customer data

Parameter Type Status Description
first_name string required The customer's first name
last_name string required The customer's last name
gender string required The customer's gender
email string required The customer's email
phonenumber string required The customer's phone number
address string optional The booker's address
city_name string optional The booker's city name
country_code string optional The booker's country code
postal_code string optional The booker's postal code
is_company boolean optional Defaults to false. If the customer is a company, set this field to true
registration_number string optional Customer's registration number. Only applies for companies
vat_number string optional Customer's vat number. Only applies for companies

# Passengers parameters

Parameter Type Status Description
first_name string required The customer's first name
middle_name string optional Customer's middle name (if applicable)
last_name string required The customer's last name
gender string required The customer's gender
is_infant boolean required If the passenger is under to years old, set to true
date_of_birth string required Passenger's date of birth
birth_country string optional Passenger's birth country
passport_number string optional Passenger's passport number
passport_expiry_date string optional Passenger's passport expiry date
passport_country_of_issue string optional Passenger's passport country of issue
nationality string optional Passenger's nationality

# Luggage options

The luggage parameter is an object whose keys must match the ids sent in the requests parameter. Each key must then have an array as a value, with the chosen luggage option. The luggage paramater has thus the following signature:

"luggage": {
  "24401": [
    {
      "weight": 20,
      "price": 0,
      "quantity": 2
    }
  ],
  "24402": [
    {
      "weight": 23,
      "price": 30.6,
      "quantity": 2
    }
  ]
}

Each luggage option contains the same three parameters:

Parameter Type Status Description
price number required the price of the chose luggage option
quantity number required the number of the chosen lugagge option. The quantity must not exceed the number of passengers
weight number required the weight of the chosen luggage option

Example request

curl 'https://booking-api.citizenplane.com/v2/bookings' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_token}' \
-d '{
  "requests": [ 244401, 244402 ],
  "customer_code": "edreams",
  "external_id": "ABC123",
  "first_name": "John",
  "last_name": "Doe",
  "gender": "male",
  "email": "john.doe@example.com",
  "phonenumber": "+33600000000",
  "passengers": [
    {
      "first_name": "John",
      "last_name": "Doe",
      "gender": "male",
      "is_infant": false,
      "date_of_birth": "1987-06-21"
    }
  ],
  "luggage": {
    "244402": [
      {
        "weight": 20,
        "quantity": 1,
        "price": 0
      }
    ]
  },
  "payment_method": "pm_1IxrDoCLoBt04Rh8deU0seLG",
  "three_d_secure": {
    "version": "2.2.0",
    "electronic_commerce_indicator": "05",
    "cryptogram": "4BQwsg4yuKt0S1LI1nDZTcO9vUM=",
    "transaction_id": "f879ea1c-aa2c-4441-806d-e30406466d79"
  },
  self_psp: true // TO USE ONLY IF YOUR TEAM IS AUTHORIZED TO APPLY THIS SETTING
}'

Example response

{
  "bookings": [
    {
      "id": 253571,
      "flight_id": 4014831,
      "request_id": 244401,
      "cp_reference": "CPKVLF",
      "pnr_airline": null,
      "status": "AWAITING_PNR_AIRLINE",
      "first_name": "john",
      "last_name": "doe",
      "gender": "male",
      "email": "john.doe@example.com",
      "phonenumber": "0033600000000",
      "address": "n/a",
      "country_code": "n/a",
      "postal_code": "n/a",
      "passenger_count": 1,
      "infant_count": 0,
      "passengers": [
        {
          "id": 426678,
          "booking_id": 253571,
          "gender": "male",
          "last_name": "doe",
          "first_name": "john",
          "is_infant": false,
          "date_of_birth": "01/01/2001",
          "nationality": null,
          "confirmed": false,
          "confirmed_at": null,
          "canceled": false,
          "canceled_at": null,
          "passport_number": null,
          "passport_expiry_date": null,
          "passport_country_of_issue": null,
          "created_at": "2022-06-28T00:34:21.461Z",
          "updated_at": "2022-06-28T00:34:21.317Z",
          "birth_country": null
        }
      ],
      "luggage": [],
      "cc_fee": 0,
      "luggage_fee": 0,
      "created_at": "2022-06-28T00:34:21.416Z",
      "price": 469,
      "infant_price": 126,
      "total_price": 469,
      "external_id": "ABC123",
      "flight": {
        "id": 4014831,
        "need_apis": true,
        "cabin_class": "economy",
        "booking_class": "Y",
        "included_airport_tax": 56.28,
        "duration": "07:55",
        "luggage_options": [
          {
            "weight": 20,
            "quantity": 1,
            "price": 35
          }
        ],
        "departure_date": "2022-08-06 13:45:00",
        "arrival_date": "2022-08-06 22:40:00",
        "origin": {
          "iata_code": "CDG",
          "city_name": "Paris",
          "country_name": "France",
          "timezone": "Europe/Paris"
        },
        "destination": {
          "iata_code": "AYT",
          "city_name": "Antalya",
          "country_name": "Turkey",
          "timezone": "Europe/Istanbul"
        },
        "segments": [
          {
            "via_airport": null,
            "origin": {
              "iata_code": "CDG",
              "city_name": "Paris",
              "country_name": "France",
              "timezone": "Europe/Paris"
            },
            "destination": {
              "iata_code": "WAW",
              "city_name": "Warsaw",
              "country_name": "Poland",
              "timezone": "Europe/Warsaw"
            },
            "operating_carrier_flight_number": 1346,
            "operating_carrier": {
              "name": "Air France",
              "iata_code": "AF"
            },
            "marketing_carrier_flight_number": 1346,
            "marketing_carrier": {
              "name": "Air France",
              "iata_code": "AF"
            },
            "departure_date": "2022-08-06 13:45:00",
            "arrival_date": "2022-08-06 16:05:00"
          },
          {
            "via_airport": null,
            "origin": {
              "iata_code": "WAW",
              "city_name": "Warsaw",
              "country_name": "Poland",
              "timezone": "Europe/Warsaw"
            },
            "destination": {
              "iata_code": "AYT",
              "city_name": "Antalya",
              "country_name": "Turkey",
              "timezone": "Europe/Istanbul"
            },
            "operating_carrier_flight_number": 421,
            "operating_carrier": {
              "name": "Sunexpress",
              "iata_code": "XQ"
            },
            "marketing_carrier_flight_number": 421,
            "marketing_carrier": {
              "name": "Sunexpress",
              "iata_code": "XQ"
            },
            "departure_date": "2022-08-06 18:40:00",
            "arrival_date": "2022-08-06 22:40:00"
          }
        ]
      }
    },
    {
      "id": 253572,
      "flight_id": 3795943,
      "request_id": 244402,
      "pnr_reference": "CPKVLF",
      "pnr_airline": null,
      "status": "AWAITING_PNR_AIRLINE",
      "first_name": "john",
      "last_name": "doe",
      "gender": "male",
      "email": "john.doe@example.com",
      "phonenumber": "0033600000000",
      "address": "n/a",
      "country_code": "n/a",
      "postal_code": "n/a",
      "passenger_count": 1,
      "infant_count": 0,
      "passengers": [
        {
          "id": 426679,
          "booking_id": 253572,
          "gender": "male",
          "last_name": "doe",
          "first_name": "john",
          "is_infant": false,
          "date_of_birth": "01/01/2001",
          "nationality": null,
          "confirmed": false,
          "confirmed_at": null,
          "canceled": false,
          "canceled_at": null,
          "passport_number": null,
          "passport_expiry_date": null,
          "passport_country_of_issue": null,
          "created_at": "2022-06-28T00:34:21.706Z",
          "updated_at": "2022-06-28T00:34:21.659Z",
          "birth_country": null
        }
      ],
      "luggage": [
        {
          "weight": 20,
          "quantity": 1,
          "price": 0
        }
      ],
      "cc_fee": 0,
      "luggage_fee": 0,
      "created_at": "2022-06-28T00:34:21.695Z",
      "price": 555,
      "infant_price": 150,
      "total_price": 555,
      "external_id": "ABC123",
      "flight": {
        "id": 3795943,
        "need_apis": false,
        "cabin_class": "economy",
        "booking_class": "Y",
        "included_airport_tax": 66.6,
        "duration": "04:20",
        "luggage_options": [
          {
            "weight": 20,
            "quantity": 1,
            "price": 0
          }
        ],
        "departure_date": "2022-08-13 13:35:00",
        "arrival_date": "2022-08-13 16:55:00",
        "origin": {
          "iata_code": "AYT",
          "city_name": "Antalya",
          "country_name": "Turkey",
          "timezone": "Europe/Istanbul"
        },
        "destination": {
          "iata_code": "CDG",
          "city_name": "Paris",
          "country_name": "France",
          "timezone": "Europe/Paris"
        },
        "segments": [
          {
            "via_airport": null,
            "origin": {
              "iata_code": "AYT",
              "city_name": "Antalya",
              "country_name": "Turkey",
              "timezone": "Europe/Istanbul"
            },
            "destination": {
              "iata_code": "CDG",
              "city_name": "Paris",
              "country_name": "France",
              "timezone": "Europe/Paris"
            },
            "operating_carrier_flight_number": 421,
            "operating_carrier": {
              "name": "Sunexpress",
              "iata_code": "XQ"
            },
            "marketing_carrier_flight_number": 421,
            "marketing_carrier": {
              "name": "Sunexpress",
              "iata_code": "XQ"
            },
            "departure_date": "2022-08-13 13:35:00",
            "arrival_date": "2022-08-13 16:55:00"
          }
        ]
      }
    }
  ]
}

# Retrieve booking and flight information

This endpoint retrieves one or two bookings (in case of round trip bookings) and flight information using the cp_reference returned at the booking step. The same cp_reference that can be used for passengers to check their reservation on our website (opens new window). All flight changes along with the final_flight will also be returned, allowing you to automatically check if the flight has received a schedule change.

Two flight objects will be returned in the response:

  • flight, which is the original flight on which the booking was initially made
  • final_flight, the actual flight, with all up to date information

GET

https://booking-api.citizenplane.com/v2/bookings

# Query parameters

Parameter Type Status Description
cp_reference string required The unique reference returned by /bookings linked to up to 2 bookings

# Flight changes parameters

The flight_changes key is an array listing all changes that occurred on the flight and their respective status which range from WAITING, SEEN, ACCEPTED or REFUSED, depending on the PAX actions.

For each flight_change object you'll have a data key containing an old & new flight, listing all keys that have changed because of the schedule change event.

As each flight can have up to three segments, each key will be prepended with segment_{index} with index ranging from 1 to 3. For instance, if the origin_airport key has changed for the first segment, it will be listed as segment_1_origin_airport. The only key that is specific for the whole trip is the need_apis key which can be either true or false.

Here are are the keys that can change for each segment:

Parameter Type Description
origin_airport string three letters IATA code (e.g CDG)
destination_airport string three letters IATA code
departure_date string YYYY-MM-DD HH:mm:ss formatted date
arrival_date string YYYY-MM-DD HH:mm:ss formatted date
flight_number string combination of two letters iata airline code and up to four digits (e.g AF1234)
origin_timezone string timezone of the origin airport, only present with the origin_airport (e.g Europe/Berlin)
destination_timezone string timezone of the origin airport, only present with the origin_airport (e.g Europe/Berlin)
airline_name string name of the arline, only present with airline_iata_code (e.g Air France)
airline_iata_code string two letters IATA code (e.g AF)
origin_terminal_label string corresponding terminal of the airport (e.g 2)
destination_terminal_label string corresponding terminal of the airport

Example request

curl 'https://booking-api.citizenplane.com/v2/bookings?cp_reference=CPKVLF' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_token}'

Example response

{
  "bookings": [
    {
      "id": 253571,
      "flight_id": 4014831,
      "request_id": 244401,
      "cp_reference": "CPKVLF",
      "pnr_airline": null,
      "passenger_count": 1,
      "infant_count": 0,
      "type": "outward",
      "status": "AWAITING_PNR_AIRLINE",
      "passengers": [
        {
          "id": 426678,
          "booking_id": 253571,
          "gender": "male",
          "last_name": "doe",
          "first_name": "john",
          "is_infant": false,
          "date_of_birth": "01/01/2001",
          "nationality": null,
          "confirmed": false,
          "confirmed_at": null,
          "canceled": false,
          "canceled_at": null,
          "passport_number": null,
          "passport_expiry_date": null,
          "passport_country_of_issue": null,
          "created_at": "2022-06-28T00:34:21.461Z",
          "updated_at": "2022-06-28T00:34:21.317Z",
          "birth_country": null
        }
      ],
      "luggage": [],
      "created_at": "2022-06-28T00:34:21.416Z",
      "flight_changes": [
        {
          "status": "ACCEPTED",
          "data": {
                  "newFlight": {
                          "flight_id": 4086743,
                          "segment_1_departure_date": "2022-08-06 14:45:00",
                          "segment_1_arrival_date": "2022-08-06 17:05:00",
                          "segment_1_origin_timezone": "Europe/Paris",
                          "segment_1_destination_timezone": "Europe/Warsaw",
                  },
                  "oldFlight": {
                          "flight_id": 4014831,
                          "segment_1_departure_date": "2022-08-06 13:45:00",
                          "segment_1_arrival_date": "2022-08-06 16:05:00",
                          "segment_1_origin_timezone": "Europe/Paris",
                          "segment_1_destination_timezone": "Europe/Warsaw",
                  }
          },
          "created_at": "2023-10-23T11:09:32.721Z",
          "updated_at": "2023-10-25T12:00:03.088Z"
        }
      "flight": {
        "id": 4014831,
        "need_apis": true,
        "cabin_class": "economy",
        "booking_class": "Y",
        "included_airport_tax": 56.28,
        "duration": "07:55",
        "luggage_options": [
          {
            "weight": 20,
            "quantity": 1,
            "price": 35
          }
        ],
        "departure_date": "2022-08-06 13:45:00",
        "arrival_date": "2022-08-06 22:40:00",
        "origin": {
          "iata_code": "CDG",
          "city_name": "Paris",
          "country_name": "France",
          "timezone": "Europe/Paris"
        },
        "destination": {
          "iata_code": "AYT",
          "city_name": "Antalya",
          "country_name": "Turkey",
          "timezone": "Europe/Istanbul"
        },
        "segments": [
          {
            "via_airport": null,
            "origin": {
              "iata_code": "CDG",
              "city_name": "Paris",
              "country_name": "France",
              "timezone": "Europe/Paris"
            },
            "destination": {
              "iata_code": "WAW",
              "city_name": "Warsaw",
              "country_name": "Poland",
              "timezone": "Europe/Warsaw"
            },
            "operating_carrier_flight_number": 1346,
            "operating_carrier": {
                    "name": "Air France",
                    "iata_code": "AF"
            },
            "marketing_carrier_flight_number": 1346,
            "marketing_carrier": {
                    "name": "Air France",
                    "iata_code": "AF"
            },
            "departure_date": "2022-08-06 13:45:00",
            "arrival_date": "2022-08-06 16:05:00"
          },
          {
            "via_airport": null,
            "origin": {
              "iata_code": "WAW",
              "city_name": "Warsaw",
              "country_name": "Poland",
              "timezone": "Europe/Warsaw"
            },
            "destination": {
              "iata_code": "AYT",
              "city_name": "Antalya",
              "country_name": "Turkey",
              "timezone": "Europe/Istanbul"
            },
            "operating_carrier_flight_number": 421,
            "operating_carrier": {
                    "name": "Sunexpress",
                    "iata_code": "XQ"
            },
            "marketing_carrier_flight_number": 421,
            "marketing_carrier": {
                    "name": "Sunexpress",
                    "iata_code": "XQ"
            },
            "departure_date": "2022-08-06 18:40:00",
            "arrival_date": "2022-08-06 22:40:00"
          }
        ]
      },
      "final_flight": {
        "id": 4086743,
        "need_apis": true,
        "cabin_class": "economy",
        "booking_class": "Y",
        "included_airport_tax": 56.28,
        "duration": "07:55",
        "luggage_options": [
          {
            "weight": 20,
            "quantity": 1,
            "price": 35
          }
        ],
        "departure_date": "2022-08-06 14:45:00",
        "arrival_date": "2022-08-06 22:40:00",
        "origin": {
          "iata_code": "CDG",
          "city_name": "Paris",
          "country_name": "France",
          "timezone": "Europe/Paris"
        },
        "destination": {
          "iata_code": "AYT",
          "city_name": "Antalya",
          "country_name": "Turkey",
          "timezone": "Europe/Istanbul"
        },
        "segments": [
          {
            "via_airport": null,
            "origin": {
              "iata_code": "CDG",
              "city_name": "Paris",
              "country_name": "France",
              "timezone": "Europe/Paris"
            },
            "destination": {
              "iata_code": "WAW",
              "city_name": "Warsaw",
              "country_name": "Poland",
              "timezone": "Europe/Warsaw"
            },
            "operating_carrier_flight_number": 1346,
            "operating_carrier": {
                    "name": "Air France",
                    "iata_code": "AF"
            },
            "marketing_carrier_flight_number": 1346,
            "marketing_carrier": {
                    "name": "Air France",
                    "iata_code": "AF"
            },
            "departure_date": "2022-08-06 14:45:00",
            "arrival_date": "2022-08-06 17:05:00"
          },
          {
            "via_airport": null,
            "origin": {
              "iata_code": "WAW",
              "city_name": "Warsaw",
              "country_name": "Poland",
              "timezone": "Europe/Warsaw"
            },
            "destination": {
              "iata_code": "AYT",
              "city_name": "Antalya",
              "country_name": "Turkey",
              "timezone": "Europe/Istanbul"
            },
            "operating_carrier_flight_number": 421,
            "operating_carrier": {
                    "name": "Sunexpress",
                    "iata_code": "XQ"
            },
            "marketing_carrier_flight_number": 421,
            "marketing_carrier": {
                    "name": "Sunexpress",
                    "iata_code": "XQ"
            },
            "departure_date": "2022-08-06 18:40:00",
            "arrival_date": "2022-08-06 22:40:00"
          }
        ]
      }
    },
    {
      "id": 253572,
      "flight_id": 3795943,
      "request_id": 244402,
      "pnr_reference": "CPKVLF",
      "pnr_airline": null,
      "passenger_count": 1,
      "infant_count": 0,
      "type": "return"
      "status": "AWAITING_PNR_AIRLINE",
      "passengers": [
        {
          "id": 426679,
          "booking_id": 253572,
          "gender": "male",
          "last_name": "doe",
          "first_name": "john",
          "is_infant": false,
          "date_of_birth": "01/01/2001",
          "nationality": null,
          "confirmed": false,
          "confirmed_at": null,
          "canceled": false,
          "canceled_at": null,
          "passport_number": null,
          "passport_expiry_date": null,
          "passport_country_of_issue": null,
          "created_at": "2022-06-28T00:34:21.706Z",
          "updated_at": "2022-06-28T00:34:21.659Z",
          "birth_country": null
        }
      ],
      "luggage": [
        {
          "weight": 20,
          "quantity": 1,
          "price": 0
        }
      ],
      "created_at": "2022-06-28T00:34:21.695Z",
      "flight_changes": null,
      "flight": {
        "id": 3795943,
        "need_apis": false,
        "cabin_class": "economy",
        "booking_class": "Y",
        "included_airport_tax": 66.6,
        "duration": "04:20",
        "luggage_options": [
          {
            "weight": 20,
            "quantity": 1,
            "price": 0
          }
        ],
        "departure_date": "2022-08-13 13:35:00",
        "arrival_date": "2022-08-13 16:55:00",
        "origin": {
          "iata_code": "AYT",
          "city_name": "Antalya",
          "country_name": "Turkey",
          "timezone": "Europe/Istanbul"
        },
        "destination": {
          "iata_code": "CDG",
          "city_name": "Paris",
          "country_name": "France",
          "timezone": "Europe/Paris"
        },
        "segments": [
          {
            "via_airport": null,
            "origin": {
              "iata_code": "AYT",
              "city_name": "Antalya",
              "country_name": "Turkey",
              "timezone": "Europe/Istanbul"
            },
            "destination": {
              "iata_code": "CDG",
              "city_name": "Paris",
              "country_name": "France",
              "timezone": "Europe/Paris"
            },
            "operating_carrier_flight_number": 421,
            "operating_carrier": {
                    "name": "Sunexpress",
                    "iata_code": "XQ"
            },
            "marketing_carrier_flight_number": 421,
            "marketing_carrier": {
                    "name": "Sunexpress",
                    "iata_code": "XQ"
            },
            "departure_date": "2022-08-13 13:35:00",
            "arrival_date": "2022-08-13 16:55:00"
          }
        ]
      },
      "flight": {
        "id": 3795943,
        "need_apis": false,
        "cabin_class": "economy",
        "booking_class": "Y",
        "included_airport_tax": 66.6,
        "duration": "04:20",
        "luggage_options": [
          {
            "weight": 20,
            "quantity": 1,
            "price": 0
          }
        ],
        "departure_date": "2022-08-13 13:35:00",
        "arrival_date": "2022-08-13 16:55:00",
        "origin": {
          "iata_code": "AYT",
          "city_name": "Antalya",
          "country_name": "Turkey",
          "timezone": "Europe/Istanbul"
        },
        "destination": {
          "iata_code": "CDG",
          "city_name": "Paris",
          "country_name": "France",
          "timezone": "Europe/Paris"
        },
        "segments": [
          {
            "via_airport": null,
            "origin": {
              "iata_code": "AYT",
              "city_name": "Antalya",
              "country_name": "Turkey",
              "timezone": "Europe/Istanbul"
            },
            "destination": {
              "iata_code": "CDG",
              "city_name": "Paris",
              "country_name": "France",
              "timezone": "Europe/Paris"
            },
            "operating_carrier_flight_number": 421,
            "operating_carrier": {
                    "name": "Sunexpress",
                    "iata_code": "XQ"
            },
            "marketing_carrier_flight_number": 421,
            "marketing_carrier": {
                    "name": "Sunexpress",
                    "iata_code": "XQ"
            },
            "departure_date": "2022-08-13 13:35:00",
            "arrival_date": "2022-08-13 16:55:00"
          }
        ]
      }
    }
  ]
}

# Patch PNR

The patchPNR endpoint is designed to update the pnr_airline field of a specific booking. If the airline PNR is not returned at booking time, then you should call this endpoint as soon as possible, once the PNR is available.

The updated booking will be returned in the response.

PATCH

https://booking-api.citizenplane.com/v2/bookings/{pnr}/pnr

# Parameters

Parameter Type Status Description
pnr string required The internal CP pnr reference of the booking to be updated (ie starting with CP).

# Rules for PNR Identification

The PNR is not a unique identifier. To manage different travel segments, we follow these rules:

  • One-way: CPPNR
  • Outward: CPPNR-1
  • Return: CPPNR-2
# Examples:
  • A one-way trip will be identified as CP1234.
  • An outward trip will be identified as CP1234-1.
  • A return trip will be identified as CP1234-2.

# Payload

Parameter Type Status Description
pnr_airline string required The new PNR to update the booking with.

Example request

curl 'https://booking-api.citizenplane.com/v2/bookings/ABCDEF/pnr' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_token}'
-d '{
	"pnr_airline": "XXXXXX"
}'

Example response

{
  "id": 860451,
  "flight_id": 122649828,
  "request_id": 1533287,
  "pnr_reference": "CP9I3P",
  "first_name": "benoit",
  "last_name": "bruynbroeck",
  "gender": "male",
  "email": "benoit@citizenplane.com",
  "phonenumber": "610433851033",
  "address": "n/a",
  "country_code": "en",
  "postal_code": "n/a",
  "passenger_count": 0,
  "infant_count": 0,
  "pnr_airline": "XXXXXX"
}