- Developers
- General conventions
General conventions
This page details the foundational technical conventions governing all Ferry APIs. Understanding rules on data formats, cursor pagination, sorting constraints, and duplicate protection helps you build a resilient integration layer and avoid operational pitfalls.
Paths, versioning, and request payloads
Section titled “Paths, versioning, and request payloads”The Ferry platform adheres to the following communication standards:
| Item | Technical specification | Integration guidance |
|---|---|---|
| Prefix and version | /api/v1/<resource> | All current APIs operate on version v1 |
| Payload size limit | Maximum 1 MB per request | Optimize request payloads for large passenger groups |
| Schema filtering (whitelist) | Undeclared request fields are silently stripped | Misspelled field names are discarded without error messages |
| Data validation error | HTTP 422 Unprocessable Entity | Distinct from 400; 422 indicates data constraint violations |
| Request trace identifier | x-request-id header | Generate a unique UUID per call to facilitate log investigations |
- Responses are compressed automatically except for event streams; your HTTP client must accept compressed content.
- Message language is selected through the language header and defaults to Vietnamese.
- Ferry generates a trace ID if
x-request-idis omitted. x-public,x-internal, andx-adminare OpenAPI documentation labels only; sending them does not change access permissions.- API-key responses may omit fields that the Dashboard displays even when you have access to the record. This can be a missing partner-field declaration; report it to Ferry, as there is no client-side workaround.
Cursor pagination
Section titled “Cursor pagination”Composite cursor pagination is the default. Page-number pagination still exists in some legacy groups; that format returns a next-page flag and total record count rather than a cursor.
The diagram below illustrates the cursor pagination workflow between the partner system and Ferry:
sequenceDiagram autonumber participant Partner as Partner System participant Ferry as Ferry API Partner->>Ferry: Initial request with limit parameter Ferry-->>Partner: Return record list with nextCursor loop While nextCursor is present Partner->>Ferry: Subsequent request with cursor=nextCursor Ferry-->>Partner: Next page of records with updated nextCursor end Note over Partner,Ferry: Stop when nextCursor is null or target count reachedOperational pagination rules:
- Record ceiling (
limit): Defaults to 10 records per page. The maximum ceiling is 50 records/page (the HTML voyage table allows up to 100). Sendinglimit=200silently clamps down to 50 records without throwing an error. - Cursor nature: The
nextCursortoken encodes pagination state based on sort ordering and record identifiers. Use this token only within your current pagination loop; do not persist cursors in long-term database storage. - Malformed cursor handling: Submitting an invalid cursor silently resets retrieval to the first page rather than returning an error. Your client loop must incorporate safety checks (such as maximum iteration bounds) to avoid infinite loops.
Sorting and filtering
Section titled “Sorting and filtering”Sorting limitations
Section titled “Sorting limitations”The Ferry platform enforces default sorting by descending record identifier across all collection endpoints.
If your client interface requires alternative presentation orders (such as lowest price first or earliest departure), fetch the complete record set to your server and perform local sorting before rendering.
Filtering mechanics
Section titled “Filtering mechanics”Endpoints support filtering on predefined attributes documented for each resource (e.g. booking status, date intervals, or partner identifiers). Rely on standard query parameters documented in Scalar rather than custom internal parameters.
Dynamic filters support 13 operators: eq, neq, contains, ncontains, gt, gte, lt, lte, in, nin, between, empty, nempty. Conditions are combined with AND, not OR. Fields must be on the resource’s allow-list; the groups with such lists are bookings, customers, organizations, routes, transactions, users, and locations.
The booking list returns a deprecation warning. Dynamic filters can still be applied, but no replacement group exists at the partner tier. Do not build logic that depends on these filters.
Dates, time zones, and currency
Section titled “Dates, time zones, and currency”| Attribute | Standard specification | Handling rule |
|---|---|---|
| Search date parameter | YYYY-MM-DD | Submit date strings according to local Vietnam time (GMT+7) |
| Returned timestamps | ISO-8601 string in UTC | Convert to GMT+7 before presenting to end customers |
| Currency unit | Vietnamese Dong (VND) | Stored and returned as integers without decimal fractions |
The most frequent integrator error is directly displaying raw UTC timestamps in end-user interfaces, resulting in departure times showing 7 hours earlier than actual schedule.
Payloads have no separate currency-unit or business-time-zone field. All monetary amounts are VND; bare dates use the operator’s local time, not your server’s time zone. The business day changes at midnight Vietnam time.
Idempotency and duplicate prevention
Section titled “Idempotency and duplicate prevention”Ferry does not support automated idempotency key headers for booking creation calls.
Safe implementation guidelines for partners:
- Maintain business reference codes: Generate an external transaction reference ID on your end for every customer order.
- Inspect state before retrying: Upon experiencing a connection interruption during booking creation, query the recent bookings list to verify whether the order was recorded before issuing a retry.
- Internal ticketing locks: For ticket issuance confirmation calls, Ferry maintains internal booking-level concurrency locks to prevent duplicate balance deductions or simultaneous double issuance.
A confirmation retried while work is running usually returns an in-progress error, not a replay of the previous result. Webhooks also have no deduplication key; deduplicate using the resource identifier in the event. See Webhooks.
Empty response lists and error handling
Section titled “Empty response lists and error handling”When querying lists (voyages, orders, transactions), responses may return an empty array [] accompanied by HTTP 200.
Important considerations:
- An empty list may indicate no matching records exist (sailings sold out or not yet opened), but can also occur if backend database connections encounter transient interruptions.
- Do not use a single empty query response as justification to purge locally cached synchronization data. If an empty list appears unexpectedly, retry after several seconds or verify with alternative queries.
Common misconceptions
Section titled “Common misconceptions”| Misconception | Reality | Correct practice |
|---|---|---|
| Requesting sailings sorted by cheapest fare | The system always returns records sorted by ID descending | Fetch data to your server and sort locally in your app |
| Requesting 200 records per page | The system clamps page size to a maximum of 50 | Iterate through subsequent pages using cursor pagination |
| Automatically retrying booking creation on network blips | Produces duplicate bookings and holds ghost seats | Query recent bookings before deciding whether to retry |
| Presenting API timestamps directly to customers | Timestamps are formatted in UTC | Convert timestamps to Vietnam local time (GMT+7) |
| Treating every HTTP 422 as a payload-format error | Status 422 also covers business failures, insufficient funds, and work already in progress | Inspect the code and message; do not retry automatically |
Where to look in Scalar
Section titled “Where to look in Scalar”This guide relates to common query parameters present across most endpoint groups on the Scalar UI — Ferry VN API:
| Parameter in Scalar | Description and convention |
|---|---|
limit | Desired record count per page (defaults to 10, maximum 50) |
cursor | Cursor string obtained from nextCursor on previous page |
x-request-id | Optional header carrying a client-generated request correlation ID |
Read next
Section titled “Read next”| Guide | Recommended next reading |
|---|---|
| Authentication with an API key | Header format specifications and authentication error codes |
| Bookings: the life cycle of one booking | Applying duplicate-order prevention to the booking workflow |
| Fares and multi-level markup | Currency rounding conventions and fare structure details |
| Errors and how to handle them | Complete error code reference and safe retry patterns |