Skip to content

Ticket pricing and multi-level markup

5 min read

An order within the Ferry ecosystem carries three distinct price figures depending on the perspective: the operator base price, the partner NET rate payable to upstream organizations, and the retail price paid by the end customer. The margin between these figures compounds along each tier of the ticket distribution hierarchy.

The diagram below shows the three pricing perspectives and their corresponding settlement parties for a single order:

flowchart LR
O["Single ticket order"]
O --> G1["Operator base price<br/>(all markups excluded)"]
O --> G2["Partner NET rate<br/>(upstream markups included)"]
O --> G3["Retail customer price<br/>(all markups included)"]
G1 --> X1["Ferry pays operator"]
G2 --> X2["You pay upstream partner"]
G3 --> X3["Customer pays you"]
X3 --> V1["Vouchers and discounts apply here"]
G2 --> V2["No vouchers applied"]
G1 --> V3["No vouchers applied"]

These three figures represent three distinct commercial relationships:

  • Operator base price: The baseline fare published by the ferry operator, free of any agency commissions or markups.
  • Partner NET rate: The cost your organization must settle with your direct upstream parent. This amount accumulates markups from all ancestor organizations above you.
  • Retail customer price: The total fare charged to the passenger, including applicable markups and discounts. The customer perspective uses a tax-inclusive price, while the agency perspective uses the tax-exclusive base price. The difference between retail and NET prices therefore does not represent your markup alone.

The diagram below illustrates how price additions accumulate down the agency hierarchy:

flowchart TD
R["Root organization"] -->|"Add root surcharge"| L1["Tier 1 agency"]
L1 -->|"Add Tier 1 surcharge"| L2["Tier 2 agency"]
L2 -->|"Add Tier 2 surcharge"| U["Your organization"]
U -->|"Add your own surcharge"| F["Final retail price"]
F --> V["Deduct voucher if valid"]
R -.->|"Strip all surcharges"| N["Operator base price"]
U -.->|"Excludes your surcharge"| M["Partner NET rate"]

Compounding percentage accumulation: When an organization tier configures a surcharge as a percentage, that percentage is applied against the price that already incorporates surcharges from all preceding upstream tiers.

A crucial consequence for revenue reconciliation: the overall chain markup rate does not equal the simple arithmetic sum of the percentages. For instance, a three-tier agency chain where each tier adds 10% produces an effective compounded increase of 33.1%, not 30%. When deducted amounts differ from initial estimates, verify this compounding rule before investigating discounts or tax rules.

The diagram below outlines the sequence of API calls used to validate pricing and discounts before booking:

sequenceDiagram
participant P as "Your system"
participant F as "Ferry API"
participant C as "Pricing engine"
P->>F: "Check additions for order context"
F->>C: "Aggregate surcharges along org chain"
C-->>F: "Retail price and partner NET rate"
F-->>P: "Both price figures with margin"
P->>F: "Check voucher applicability"
alt Voucher is applicable
F-->>P: "Valid code with discount amount"
else Not applicable
F-->>P: "Rejection reason, retain original price"
end
P->>F: "Create new booking"
F-->>P: "Booking ID and recorded price"

The table below summarizes the actions at each step:

StepYour systemFerry APIPartner takeaway
1Define specific order contextCompares context against configured surcharge rulesProvide exact voyage code, seat tier, and passenger count
2Call addition check (POST /ticket-price-additions/check)Calculates and returns NET rate alongside retail priceThis check does not require special administrative privileges
3Call voucher check (POST /vouchers/check-aplicability)Returns applicability status and discount amountNote the identifier spelling check-aplicability
4Evaluate gross marginCompares the gap between retail price and NET rateConfirm margin covers operational processing costs
5Create booking and confirm issuanceRecords booking and recalculates prices upon issuanceDebited funds are finalized at the moment of ticket issuance

Both addition and voucher checks require a valid API key but no special permission. Creating additions or vouchers requires the corresponding create permission. Check prices using the exact voyage, seat class, passenger count, and passenger type; an existing addition may not apply when its conditions do not match.

  • Recalculation at issuance: Ferry re-evaluates all fare components at the exact moment ticket confirmation is executed rather than relying on search-time prices. Prices seen during search are advisory estimates.

  • Currency conventions: All monetary figures are integers in Vietnamese Dong (VND), without fractional units or separate currency fields.

  • Voucher check endpoint naming: The voucher validation endpoint uses the exact path POST /vouchers/check-aplicability (with a single ‘p’). Typing standard English spelling results in an HTTP 404.

  • Vouchers apply only to retail price: Applying a voucher reduces the passenger’s payable amount; it never reduces your NET settlement cost with your upstream parent.

  • No idempotency on addition or voucher creation: Retrying after a timeout can create a second record. List existing records and compare them with the submitted data; reuse a matching record and create again only when none is found. Duplicate additions can charge the customer twice.

  • The default perspective is the end customer: Without an explicit perspective, the returned price is for quoting to the customer, not for settling with your upstream parent.

  • Price figures available to partners: Booking retail total, booking NET total, original total before a ticket change, NET total of each order leg, and the order’s refunded NET amount. Do not assume all other price fields are exposed to API keys; report an expected missing field to Ferry rather than retrying.

  • Do not assume rounding rules: The order of applying additions, tax, and vouchers, and the rounding rules, have not been fully confirmed. Reconcile using Ferry’s returned figures; availability of cost, profit, and revenue also requires confirmation from Ferry.

SymptomProbable causeRecommended actionRetry safe
Retail price equals partner NET rateYour organization has not created its own addition ruleConfigure an addition rule via the API or dashboardNo, configure addition first
Voucher check returns HTTP 404Typo in the endpoint path using standard spellingUse the exact path check-aplicabilityYes, after fixing URL
Voucher reports not applicableOrder does not satisfy date, route, or seat tier criteriaInspect the rejection reason in the responseNo, inspect conditions
Debited amount differs from search priceSystem recalculated prices at confirmation timeUse the confirmation response price for final accountingNo, this is standard behavior
Surcharges duplicated after network timeoutSurcharge creation was retried without checking existing recordsDelete duplicate addition and verify pricingDo not retry blind creates

To inspect exact request schemas and test calls directly, open the Scalar UI under the relevant tags:

CapabilityScalar tag
Configure and verify price additionsTicket Price Additions
Manage and validate vouchersVouchers
Inspect price fields on bookingsBookings
View NET rates on individual order legsOrders

For instructions on configuring your API key in Scalar, see API Reference.

PageRelated topic
Voyage search and remaining seatsDiscovering voyages and base timetables
The booking flowComplete booking lifecycle and payment moments
Payments and balancesBalance deduction mechanics and credit limits
Ticket issuanceStep-by-step electronic ticketing execution
General conventionsCurrency formatting and timeout handling