# 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.ggovoyagesfrfor GoVoyages.fr (opens new window),kiwicomfor 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_dataobject sent withhttps://booking-api.citizenplane.com/v2/bookingspayload 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 |
| 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 |
| 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 madefinal_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"
}