Overview

Duffel uses standard HTTP response codes to indicate the success or failure of API requests.

Status code

CodeReasonDescription
200OKThe request was successful
201CreatedThe request was successful, and a new resource was created
202AcceptedThe request was successful, but the processing hasn't been completed. You should not retry this request.
204No ContentThe request was successful, but there is no response to send back
400Bad RequestThe request was invalid, for example due to missing headers
401UnauthorizedAn access token wasn't provided, or the provided token was invalid
403ForbiddenA valid access token was provided, but it didn't have sufficient permissions
404Not FoundThe requested resource doesn't exist
406Not AcceptableThe response type you requested with your Accept header isn't supported
422Unprocessable EntityA validation error occurred
429Too Many RequestsYou made too many requests to the API in a short period of time
500Internal Server ErrorSomething went wrong. Please contact our support team and attach the request_id to your message. You should not retry this request.
502Bad GatewayBad gateway error. Please contact our support team and attach the request_id to your message. You should not retry this request.
503Service UnavailableThere is a temporary issue with the server. If the error persists please contact our support team and attach the request_id to your message. Please retry later.
504Gateway TimeoutGateway timeout error. If the error persists please contact our support team and attach the request_id to your message. Please retry later.

4XX Request Errors

There’s a problem in your request. There can be many reasons, from a malformed request (e.g. invalid JSON), invalid value in a request key (e.g. namer with integer number), or a problem with a resource (e.g. offer is expired). You should check Status Code and Errors sections below for the most common errors and how to handle them.

503 Service Unavailable

There’s an unexpected temporary problem. We know for sure that no booking was created in the supplier systems. This indicates you can retry the request again at another time or start another search. If the error persists, you should contact our support team and attach the request_id to your message.

500 Internal Server Error

An unexpected problem has occurred. This is often due to a temporary issue on the supplier systems. Regardless of the cause of the issue, we actively monitor such failures and will promptly investigate, address, and mitigate these issues, or reach out to our customers to provide a workaround. If you consistently encounter this error with a specific request, don't hesitate to contact our support team for more details on the failed attempt. Remember to include the request_id, as well as the id of the resource you're trying to create (the offer id for Duffel Flights, or quote id for Duffel Stays) in your message.

Errors

Error responses

Detailed information on what exactly went wrong will be included in the response body. Every error returned by the API includes:
NameDescription
titleA quick and simple description of what went wrong
messageA more detailed human-readable description of what went wrong
documentation_urlA URL pointing to a place in our documentation where you can read about the error
typeA machine-readable identifier for the general category of error
codeA machine-readable identifier for this specific error

Error types

An error’s type is an enum of the following values:
NameDescription
authentication_errorThere was a problem with authenticating you - for example, you didn't provide an access token or it was invalid
airline_errorWe've received an error back from the airline - for example, your booking has already been cancelled
invalid_state_errorYou tried to perform an action on a resource that wasn't appropriate - for example, you tried to create an order with an offer that is no longer available
rate_limit_errorYou made too many requests to the API in a short period of time
validation_errorYou didn't provide a required parameter or a parameter you provided was invalid - for example, you didn't specify slices when creating an offer request
invalid_request_errorThere was some other kind of problem with your request - for example, you requested a resource that doesn't exist or missed out a required header
api_errorSomething went wrong on our side, and has been reported to us

Error codes

An error's code is an enum of the following values:

General error codes

Applies to all endpoints.

Authentication errors

The table below lists all possible code values for type authentication_error.
HTTP StatusValueDescription
401access_token_not_foundThe access token you have used is not a valid API access token
401expired_access_tokenThe access token set in the 'Authorization' header is expired
401invalid_authorization_headerThe 'Authorization' header should have the following format 'Bearer API_TOKEN'
401missing_authorization_headerThe 'Authorization' header needs to be set and contain a valid API token
403insufficient_permissionsThe token attached to this request does not have sufficient permissions to perform this action

Invalid request errors

The table below lists all possible code values for type invalid_request_error.
HTTP StatusValueDescription
400bad_requestThe request was unacceptable
400invalid_content_type_headerThe 'Content-Type' should be set to 'application/json'
400missing_content_type_headerThe 'Content-Type' header needs to be set to 'application/json'
400invalid_version_headerThe 'Duffel-Version' header needs to be set to a valid API version
400missing_version_headerThe 'Duffel-Version' header needs to be set to a valid API version
400unsupported_versionThe version set in the 'Duffel-Version' header is no longer supported by the API. Please upgrade
400unavailable_in_versionThis feature is unavailable in this API version. Please upgrade
400invalid_data_paramThe data in the request body should be a JSON object
422malformed_data_paramThe data in the request body contains malformed content
400missing_data_paramThe data in the request body should be nested under a 'data' key
404not_foundThe resource you are trying to access does not exist
406unsupported_formatThe API does not support the format set in the 'Accept' header. Please use a supported format

Rate limit errors

The table below lists all possible code values for type rate_limit_error.
HTTP StatusValueDescription
429rate_limit_exceededToo many requests hit the API too quickly. Please retry your request after the time specified in the ratelimit-reset header

Flights endpoints

Create offer request (search)
POST /air/offer_requests

Airline errors

The table below lists all possible code values for type airline_error.
HTTP StatusValueDescription
502airline_internalThe airline responded with an internal error, please contact support
502airline_unknownThe airline responded with an unexpected error, please contact support
504airline_internalThe airline responded with a timeout error (when caused by timeout)
Note: airline_internal returns 502 for general internal errors and 504 when caused by timeouts.

Invalid state errors

HTTP StatusValueDescription
422offer_request_already_bookedAn offer from this offer request has already been booked; please perform a new search
422order_creation_already_attemptedOrder creation has already been attempted for an offer from this request; please perform a new search
Get offer
GET /air/offers/:id

Invalid state errors

HTTP StatusValueDescription
422invalid_intended_cardThe intended card attached to the offer is invalid. Please price again with a valid card record
Create order
POST /air/orders

Airline errors

The table below lists all possible code values for type airline_error.
HTTP StatusValueDescription
422offer_no_longer_availableThe provided offer is no longer available, please select another offer or create a new offer request to get the latest availability
422price_changedThe provided offer is no longer available for the same price, please retrieve the offer again to get the latest pricing information
422payment_declinedThe payment was declined, please try again with a different payment method
422duplicate_bookingA booking with the same details was already found for the selected itinerary, please select another offer
422duplicate_passenger_namesThe order cannot contain more than one passenger with the same name
422ancillary_service_not_availableRequested ancillary service item(s) (e.g. seats) are no longer available, please update your requested services or create a new offer request
502invalid_orderThis order is invalid. Reasons can be an order with no passengers, missing fields or cancelled
422modified_externallyThe order has been modified by an external system and can no longer be retrieved
422invalid_card_expiration_dateThe card has an invalid expiration date
422ineligible_airline_creditThe airline has rejected the application on an airline credit provided
422invalid_email_addressThe airline does not support the format of the email address provided
422invalid_phone_numberThe phone number is not valid
422invalid_passenger_titleThe title of one of the passengers is not valid
422invalid_passenger_nameThe passenger name format is not valid
422invalid_loyalty_cardThe airline did not recognise the loyalty programme account details for one or more of the passengers
422order_passengers_incompatible_with_offerOrder request passengers are incompatible with the offer being booked

Invalid state errors

HTTP StatusValueDescription
422offer_expiredThe selected offer has already expired
422insufficient_balanceThere wasn't enough balance in the wallet for the operation
422three_d_secure_session_not_foundThe 3DS session was not found
422three_d_secure_session_not_ready_for_paymentThe 3DS session is not ready for payment. Check if a challenge is required, or start a new session
422three_d_secure_session_expiredThe 3DS session has expired. Please start a new session
422payment_amount_does_not_match_order_amountThe amount provided in the payment does not match the total_amount of the order
422payment_currency_does_not_match_order_currencyThe currency provided in the payment does not match the total_currency of the order
422order_not_createdThe request to create an order was not successful. You should not retry this request
Cancel order
POST /air/order_cancellations

Airline errors

The table below lists all possible code values for type airline_error.
HTTP StatusValueDescription
422already_cancelledThis order has already been cancelled
502invalid_orderThis order is invalid. Reasons can be an order with no passengers, missing fields or cancelled
422modified_externallyThe order has been modified by an external system and can no longer be retrieved

Invalid state errors

HTTP StatusValueDescription
422already_cancelledThe provided order has already been cancelled
422order_not_cancellableThe provided order cannot be cancelled
Create order change request
POST /air/order_change_requests

Invalid state errors

HTTP StatusValueDescription
422order_not_changeableThis order cannot be changed through the API
422order_not_changeable_yetChanges to this order are not permitted at this time. The airline does not allow modifications within 24 hours of placing the order
Confirm order change
POST /air/order_changes/:id/actions/confirm

Invalid state errors

HTTP StatusValueDescription
422order_change_already_actionedThe order change has already been actioned and cannot be actioned again
Order change action endpoints
POST /air/order_changes/:id/actions/*

Invalid request errors

The table below lists all possible code values for type invalid_request_error.
HTTP StatusValueDescription
422unsupported_actionThe resource does not support the following action

Stays endpoints

Search for Accommodation POST /stays/search

Invalid State Errors

HTTP StatusValueDescription
404not_foundOne or more of the accommodation IDs provided are invalid

Validation Errors

HTTP StatusValueDescription
422check_out_not_after_check_inThe check_out_date must be after the check_in_date
422rooms_must_have_at_least_one_adultThe number of adult guests must be at least equal to the number of rooms
422stay_too_longThe stay duration exceeds the maximum allowed length (99 nights)
Fetch all rates
POST /stays/search_results/:id/actions/fetch_all_rates

Invalid state errors

HTTP StatusValueDescription
422result_no_longer_availableThe Stays search result is no longer available, please perform a new search to get the latest availability
Create quote
POST /stays/quotes

Invalid state errors

HTTP StatusValueDescription
422rate_unavailableThe Stays rate for the selected accommodation is no longer available, please select another rate or do a new search to get the latest availability
422loyalty_programme_requiredThe Stays quote you are trying to book is only available to members of supported loyalty programme. Retry the booking with loyalty programme information
422loyalty_programme_unsupportedThe Stays quote you are trying to book does not support loyalty programme. Retry the booking with no loyalty programme information
422loyalty_programme_account_number_invalidThe supplier has declined the given loyalty programme account number. Retry the request with a valid loyalty_programme_account_number
Create booking
POST /stays/bookings

Invalid state errors

HTTP StatusValueDescription
422booking_already_attemptedA booking has already been attempted for this rate and is pending. If confirmed, a webhook will follow. If unsuccessful, support will contact you
422booking_already_confirmedA booking has already been confirmed for this rate. A webhook notification will follow

Validation errors

The table below lists all possible code values for type validation_error.
HTTP StatusValueDescription
422card_missingYou must provide a card ID to book the Stays quote
422card_not_foundThe card ID provided to book the Stays quote was not found
422card_not_multi_useThe card provided was not set as a multi-use card. Please retry the request and supply the ID of a multi-use card
422cannot_pay_with_cardYou cannot pay with a card for this Stays quote, retry the request without a card ID
422card_expires_before_check_inThe card provided expires before the check-in date of the Stays booking, try a different card that expires at least month after the check in date
422card_payment_not_supportedCard payments are not supported for this Stays rate, select a different rate
422card_type_not_acceptedThe card type or brand of the provided card was not accepted to book the Stays quote, select a different rate or try a different card brand
Cancel booking
POST /stays/bookings/:id/actions/cancel

Invalid state errors

HTTP StatusValueDescription
422already_cancelledThe provided booking has already been cancelled
422too_late_to_cancelIt is too late to cancel this booking

Examples

If you don't provide an authorization header in your request, you'll receive an authentication_error like the following:

JSON

{
"errors": [
{
"code": "missing_authorization_header",
"documentation_url": "https://duffel.com/docs/api/overview/response-handling",
"message": "The 'Authorization' header needs to be set and contain a valid API token.",
"title": "Missing authorization header",
"type": "authentication_error"
}
],
"meta": {
"request_id": "FZW0H3HdJwKk5HMAAKxB",
"status": 401
}
}
If you don't provide a required parameter, or some data you provided is invalid, you'll receive a validation error, with a type of validation_error. Validation errors include an additional source property, pointing to the exact field in your request which was invalid. Here's an example of a validation error returned when a slice in an offer request doesn't have an origin:

JSON

{
"errors": [
{
"code": "validation_required",
"documentation_url": "https://duffel.com/docs/api/overview/response-handling",
"message": "Field 'origin' can't be blank",
"source": {
"field": "origin",
"pointer": "/slices/0/origin"
},
"title": "Required field",
"type": "validation_error"
}
],
"meta": {
"request_id": "FZW0cz5rZoJSEekAAK2B",
"status": 422
}
}

Error cases guidelines

Some common error cases and possibly misleading scenarios that can happen and how to react to them.

Expired offers

One of the common errors that can be received while creating an order is offer_expired. This error indicates that the selected offer has expired on the Duffel platform and cannot be used to create an order.
If an offer_expired error is received while creating an order, the request should not be retried and a new search should be performed and the resulting offers used instead.
Additionally, the expires_at attribute can be used to ensure that order creation requests are not attempted with expired offers. Every offer contains an expires_at attribute that indicates how long it is valid for (usually 15 - 30 minutes from creation).

JSON

{
"data": {
...
"expires_at": "2020-01-17T10:42:14.545Z",
...
}
}
Duffel recommends pre-validation of the expires_at attribute of an offer - ensuring it is in the future - before performing an order creation request.

Passenger age vs. type mismatches

Some airlines expect an alignment of the passenger types between searching and booking flights. When this misalignment is detected by the airline, a response that indicates a lack of availability is returned and Duffel returns an offer_no_longer_available error.
While it is tricky to determine if this misalignment is the cause of a specific offer_no_longer_available error, the circumstances which generate this misalignment can be easily mitigated by passing in an age instead of type for each passenger when performing searches.
Although Duffel offers the ability to specify passengers in an offer request with a type (e.g. adult, child, infant_without_seat), airlines have varying polices and rules on how to interpret these types. As a result of these variations, there can be occasional mismatches between the passenger types in the search and the eventual order creation.

JSON

{
"data": {
"passengers": [
{
"type": "child"
}
]
}
}
To avoid this mismatch between searching and order creation, Duffel recommends to specify the age (e.g. 18) of a passenger when creating offer requests.

JSON

{
"data": {
"passengers": [
{
"age": 13
}
]
}
}

Order and booking creation

When you submit a Create Flights Order or Create Stays Booking request to Duffel, it initiates the reservation and payment process for your travel services. As money changes hands as part of this request, it is important you handle the responses correctly as the transactions is usually non-refundable.
Airline and Accommodation APIs can occasionally be slow, taking up to 120s. You must set a HTTP client timeout of at least 130s to ensure you don’t time out on your side to ensure you receive a response.
This section outlines the possible responses and how you should handle them.

201 Created

The request was successful, and a new resource was created. You’ll get the complete resource information, including the resource id, e.g. ord_00009hthhsUZ8W4LxQgkjo for Flights Orders, or bok_0000BTVRuKZTavzrZDJ4cb for Stays Bookings. You can retrieve the resource information from the Duffel API, and it will be also visible on the Duffel Dashboard. Check the endpoints’ documentation for detailed information about their response schemas.

200 Successful

The request was successful, and a new resource has not yet been created. You’ll get a message that a booking was made in the supplier system. However, it wasn’t possible to retrieve complete information about the booking during the initial booking request. You will be able to retrieve the resource information from the Duffel API, and visible on the Duffel Dashboard once the resource is created.
This response only occurs when you make a Create Flights Order with payments.type of card. For Create Stays Booking this response may occur for any payment type.
Here is an example:

JSON

{
"data": {
"message": "The booking has been confirmed. It will appear in the system soon."
}
}
There are two methods to retrieve the complete booking information, webhooks or listing bookings.
Duffel sends Webhook notifications to keep you informed about the status and details of your created resources. You will receive a notification on your Webhook immediately when the resource information is available on the Duffel System. The notifications will contain the information that you need to match the resources you attempted to create to the reservation and to get the complete information.
To get notified about the created object, you must set up webhooks and listen to the events order.created for Duffel Flights and stays.booking.created for Duffel Stays.
You can also check manually if the resource was created using the Duffel listing APIs or accessing the Duffel dashboard.
The resource might take a couple of hours to show up on the Duffel API, so it’s important that you assure the traveller that their reservation was successful.

Flights

You will receive a order.created webhook notification when the order is created. The webhook notification will contain the offer_id and the order_id.
If the order was attempted using your own agency, you will need to determine if an order was created yourself and determine what next steps to take. You will not necessarily receive any further webhooks and Duffel will not take any further actions on the order.

Stays

You will receive a stays.booking.created notification when the booking is created. The webhook notification will contain the quote_id and the booking_id You will also receive an email to your support contact email address configured via the dashboard.

202 Accepted

The request was accepted and we are working to confirm its outcome. You should not retry this request as this may result in a duplicate order or booking.
When you make a Create Flights Order request, this response can only occur with payments.type of card. When you make a Create Stays Booking request, this response may occur for any payment type.

Flights

You will receive a webhook notification to inform you on the result of your request if it was successful and resulted in an order being created. If the request did not result in an order, you will receive a order.creation_failed notification and a notification email from our support team, advising you of this, to your support contact email address configured via the dashboard.
If the order was attempted using your own agency, you will need to determine if an order was created yourself and determine what next steps to take. You will not necessarily receive any further webhooks and Duffel will not take any further actions on the order.

Stays

You will receive a stays.booking.created notification if the booking was successfully created. If the request did not result in a booking, you will receive a stays.booking_creation_failed webhook notification. In either case, you will also receive a notification email from our support team, advising you of the outcome, to your support contact email address configured via the dashboard.
Please see our webhooks implementation guide for more information on receiving webhook notifications.

4XX Request Errors

There’s a problem in your request. There can be many reasons, from a malformed request (e.g. invalid JSON), invalid value in a request key (e.g. namer with integer number), or a problem with a resource (e.g. offer is expired). You should check Status Code and Errors sections for the most common errors and how to handle them.

503 Service Unavailable

There’s an unexpected temporary problem. We know for sure that no booking was created in the supplier systems. This indicates you can retry the request again at another time or start another search. If the error persists, you should contact our support team and attach the request_id to your message.

500 Internal Server Error

An unexpected problem has occurred. This is often due to a temporary issue on the supplier systems. Regardless of the cause of the issue, we actively monitor such failures and will promptly investigate, address, and mitigate these issues, or reach out to our customers to provide a workaround.
If you consistently encounter this error with a specific request, don't hesitate to contact our support team for more details on the failed attempt. Remember to include the request_id , as well as the id of the resource you're trying to create (the offer id for Duffel Flights, or quote id for Duffel Stays) in your message.

Rate limiting

If you send too many API requests in quick succession, you'll receive a rate_limit_error like the following:

JSON

{
"errors": [
{
"code": "rate_limit_exceeded",
"documentation_url": "https://duffel.com/docs/api/overview/response-handling",
"message": "Too many requests hit the API too quickly. Please retry your request after the time specified in the `ratelimit-reset` header.",
"title": "Rate limit exceeded",
"type": "rate_limit_error"
}
],
"meta": {
"request_id": "Fkpj57Fn-uB9b0kAANVI",
"status": 429
}
}
You'll also receive information about the rate limiting which was applied to your request in the HTTP headers which are returned as part of the response:
ratelimit-limit: 60
This is the limit of requests you can make per interval period. This period is currently set to 60 seconds but is subject to change without notice. If you feel that you may require a larger quota than this, drop us a line.
ratelimit-remaining: 0
This is the amount of requests you can still make during the current period before being rate limited.
ratelimit-reset: Tue, 24 Nov 2020 08:22:00 GMT
This is when your rate limit will be reset, in an RFC 2616 compliant human readable format.

Streaming

A subset of our endpoints support HTTP streaming. This allows you to receive and process data incrementally as it becomes available rather than waiting for the complete response. To enable streaming, configure your HTTP client to handle streaming responses. Most clients will have this functionality built-in.
You'll need to implement proper JSON streaming parsers in your application to fully benefit from this feature. This can significantly reduce latency by processing partial results as they arrive.
Note: Clients should not rely on specific chunking patterns or boundaries, as these are implementation details that may change. Only complete JSON objects are guaranteed, not the specific chunking behavior.