# Flight pricing

> What each flight price field means, the b2bPrice formula, the ACD block and how the exchange rate is presented.

Source: https://fx-port.com/docs/flights/pricing

Every flight price block exposes the same set of agency-facing money fields. They are in the offers of a search, in a priced offer, in a booking, in a quote and in a payment.

## Field meanings

| Field | Meaning |
|---|---|
| `basePrice` | The airline's base fare, **before taxes**, in the agency currency. |
| `taxesAndFees` | Taxes plus all platform-side fees, folded together. **Excludes** your agency commission. |
| `agencyCommission` | The commission of your agency on this sale. |
| `finalPrice` | What the traveler pays: `basePrice + taxesAndFees + agencyCommission`. |
| `b2bPrice` | **The amount debited from your agency balance**: `finalPrice - agencyCommission`. |
| `totalAmount` | An alias of `b2bPrice` on the order quote only. |
| `supplierCurrency`, `agencyCurrency` | The currency the supplier prices in and the currency of every amount above. |
| `additionalServices` | Paid extras (see below). |

All amounts are JSON numbers in the **currency of your agency**. The sandbox agency settles in DZD.

## The formula

~~~text
finalPrice = basePrice + taxesAndFees + agencyCommission
b2bPrice   = finalPrice - agencyCommission      (= debited from your balance)
~~~

A real Duffel offer from the sandbox (supplier prices in USD, agency in DZD):

~~~json
{
  "supplierCurrency": "USD",
  "agencyCurrency": "DZD",
  "basePrice": 16385.0,
  "taxesAndFees": 5226.2,
  "agencyCommission": 21.62,
  "finalPrice": 21632.82,
  "b2bPrice": 21611.2,
  "additionalServices": []
}
~~~

Check: `16385.00 + 5226.20 + 21.62 = 21632.82` and `21632.82 - 21.62 = 21611.20`.


> [!NOTE]
> **Rounding.** The same amount can differ by one hundredth between endpoints that round it separately: a hold had `finalPrice` `29856.0` and `b2bPrice` `29826.17` in the booking and in the payment, and `29855.99` and `29826.16` in `get_booking`. Compare amounts with a tolerance of 0.01, or use the amount of one endpoint consistently.


## The same amounts, different names

| Where | Names |
|---|---|
| Search, price, book, pay | `pricing.basePrice`, `taxesAndFees`, `agencyCommission`, `finalPrice`, `b2bPrice` (twins: `base_price` ... `b2b_price`) |
| One traveler | `travelerPricing[].price.base`, `total` (= the traveler's final price) and `b2bPrice` |
| Order quote | `orderQuote.pricing` (same names, plus `totalAmount`) |
| Payment | `paymentDetails.paymentAmount` (= `b2bPrice`) |
| `get_booking` | `booking.total_price`, `agency_commission`, and `pricingBreakdown`: `base_price`, `taxes_and_fees`, `agency_commission`, `final_price`, `b2b_price` |
| Account booking detail | `totalPrice`, `netPrice` (= `totalPrice - agencyCommission`) and `agencyCommission` |

## Exchange rate

When `supplierCurrency` differs from `agencyCurrency` (for example Duffel prices in `USD` and your agency settles in `DZD`) the rate is at the **top level** of the response, never inside `pricing`:

- search: in each supplier entry, `suppliers[n].exchangeRate` (a number);
- price, book and pay: at the top level of the response, `exchangeRate` (a number), for example `250.0`;
- quote, cancel quote and confirm cancel for **Duffel** orders: an **object** `{ "rate": 250.0, "marginPercent": 0, "fromCurrency": "USD", "toCurrency": "DZD" }`.

When both currencies are the same (Amadeus Algeria prices in DZD for a DZD agency) there is no `exchangeRate`. Do not apply the rate yourself: every amount in a response is already in the agency currency.

## Per-traveler pricing

Search, price and booking responses also have a `travelerPricing` array with a per-traveler `price` object (`base`, `total`, `b2bPrice`, and the two currencies), so you can show a breakdown. The `total` of the travelers add up to `pricing.finalPrice`, and their `b2bPrice` to `pricing.b2bPrice`. Children and infants are usually cheaper than adults: read each traveler's own price.

## Extra services

`additionalServices` lists paid extras. In a search it is a list (empty for most offers). In a priced offer it is an object with a `baggage` list: each entry has an `id`, a `type` (`checked`), a `quantity`, the weight limit, `priceAmount` in the supplier's currency and `convertedPriceAmount` in yours, and the passengers and segments it applies to. FX-Port lists them so you can show the price of an extra bag; adding one to a booking is not part of the public API.

## The `acd` block (Amadeus Algeria)

Offers of Amadeus Algeria carry `pricing.acd`. It says whether the fare is an **ACD fare**: a fare of some Algeria routes whose price is adjusted by a rule, and whose booking needs **one passport copy for every traveler**, which you send after the booking. Offers of other suppliers (Duffel) have no `acd` key.

~~~json
{ "applied": false }
~~~

| `pricing.acd.applied` | Meaning |
|---|---|
| `false` | An ordinary fare. Nothing to do. |
| `true` | An ACD fare. The offer, the priced offer and the booking say `requiresACD: true`, and the booking needs passports: see [ACD passport uploads](https://fx-port.com/docs/flights/acd-documents.md). |

An ACD fare carries more keys in `acd` (`scenario`, `A` or `B`, and amounts that show how the price was worked out). They are informational, can change without notice and are not meant for travelers: do not build on them. An agency that has an ACD-exempt company can send `acd_exempt_company_id` in a search; the fares then have `{ "applied": false, "exempt": true }`. In the sandbox, `CDG` to `ALG` on `amadeus_aqc_dz_1` returns ACD fares only.

## Related

- [Search flights](https://fx-port.com/docs/flights/search.md), [Price a flight offer](https://fx-port.com/docs/flights/price.md) and [Book a flight](https://fx-port.com/docs/flights/book.md).
- [ACD passport uploads](https://fx-port.com/docs/flights/acd-documents.md).
