- Developers
- Ticket issuance and order legs
Ticket issuance and order legs
Confirming ticket issuance is the step that turns a tentative booking into valid electronic tickets on the ferry operator’s booking engine. Prior to this step, the operator has no record of the order and the passenger has no guaranteed seat; once completed, tickets are issued and passengers are clear to board.
Prerequisites before starting
Section titled “Prerequisites before starting”| Prerequisite | Business context | Reference |
|---|---|---|
| Valid API key | All ticket confirmation and file retrieval calls require partner authentication | Authentication with an API key |
| Booking in Requested status | Order must already be created and sitting in Requested status (2) | The booking flow |
| Adequate available balance | Account balance or credit limit across the org hierarchy must cover the NET rate | Payments and balances |
| Verified operator capability | Verify whether the target operator supports automated electronic ticketing | See operator capabilities list below |
| Webhook endpoint configured | Register event URLs to receive immediate issuance notifications | Webhooks and events |
UAT can still place real orders and incur real charges. One operator always books for real in both UAT and Production, even when booking is configured off. Review Environments before testing ticket confirmation.
Ticket issuance execution sequence
Section titled “Ticket issuance execution sequence”The diagram below illustrates the sequence of actions that occur when confirming ticket issuance:
sequenceDiagram autonumber participant P as "Your system" participant API as "Ferry API" participant MONEY as "Balance check" participant OP as "Operator" participant WH as "Webhook"
P->>API: "Confirm issuance (POST /bookings/confirmToIssue/{id})" API->>API: "Verify 5 req/60s ceiling and 600s lock" API->>MONEY: "Check balance and credit limits" alt Insufficient funds MONEY-->>API: "Insufficient balance" API-->>P: "HTTP 422 insufficient balance" else Sufficient funds MONEY-->>API: "Hold funds at partner NET rate" API->>OP: "Submit ticket order to operator" alt Operator issues tickets successfully OP-->>API: "Return electronic ticket identifiers" API->>WH: "Dispatch booking.issuedTicket" API-->>P: "Booking transitions to Issued (10)" else Operator rejects or encounters error OP-->>API: "Report issuance error" API->>MONEY: "Release held funds" API->>WH: "Dispatch booking.issuedTicketError" API-->>P: "Booking transitions to Issue Error (6)" end endThe table below summarizes the actions at each step:
| Step | Your system | Ferry API | Partner takeaway |
|---|---|---|---|
| 1 | Call confirm ticket issuance | Enforces rate limits (max 5 requests per 60 seconds) | Avoid rapid-fire retries while previous call executes |
| 2 | Await execution lock | Secures a 600-second lock on the booking | Concurrent calls return HTTP 422 processing error |
| 3 | Await balance check and hold | Temporarily reserves funds based on partner NET rate | Insufficient credit at any ancestor tier halts issuance |
| 4 | Await operator communication | Invokes operator engine to reserve tickets | Processing latency varies by operator engine |
| 5 | Receive response or poll status | Advances booking to status 10 upon completion | Use periodic polling as your final verification checkpoint |
| 6 | Download electronic tickets | Provides download URLs for operator ticket documents | Deliver finalized e-tickets to passengers |
The per-booking lock prevents concurrent issuance processes, but it is not an idempotency key for the entire call. There is no advance notification of lock expiry. If balance checks fail, funds already held are released; after issuance failure, refund timing is not guaranteed. Background reconciliation skips bookings already at status 10, so finalizing that state is safe to repeat.
Verifying issued ticket status
Section titled “Verifying issued ticket status”The flowchart below outlines how to verify issuance outcomes accurately:
flowchart TD A["Call ticket confirmation endpoint"] --> B{"Response outcome"} B -- "Success" --> C["Booking transitions to status 10"] B -- "Error reported" --> D["Inspect error code to retry or abort"] B -- "Timeout / Processing" --> E["Switch to periodic verification"] E --> F["Receive ticket issued webhook event"] E --> G["Poll booking details API"] F --> G G --> H{"Booking status"} H -- "Status 10 (Issued)" --> C H -- "Other status" --> I["Await background reconciliation or contact Ferry"] C --> J["Download tickets and deliver to passenger"]Coordinating Webhooks and Polling:
- Webhooks as signals: Webhooks provide low-latency notifications as soon as tickets are issued, allowing prompt UI updates. However, webhooks can occasionally be lost due to network transit issues.
- Polling as the source of truth: Calling the booking detail API (
GET /bookings/{id}) is the authoritative verification. When the status is 10 (Issued), tickets are confirmed on the carrier system. - Automated background reconciliation: If communication drops while the operator is processing, Ferry’s background worker reconciles with the operator and updates the order status within a few minutes.
Integration invariants and rules
Section titled “Integration invariants and rules”Partial issuance failures on multi-leg bookings
Section titled “Partial issuance failures on multi-leg bookings”On round-trip or multi-leg bookings, one leg may issue successfully while a subsequent leg is rejected by the operator:
- The booking does not transition to status 10; it retains an error state.
- Your balance is partially debited for the leg that issued successfully, while the failed leg amount is refunded.
- Resolution procedure: Inspect individual leg orders inside the booking (
GET /bookings/{id}) to identify issued legs, then contact Ferry with the booking code. Do not create replacement bookings or retry confirmation to repair a partial failure: confirmation reruns the entire chain, not just the missing leg.
Fare recalculation at issuance
Section titled “Fare recalculation at issuance”Ferry recalculates all fare components at the moment ticket confirmation is called rather than locking in search-time prices. If operator tariffs or addition rules have changed, the actual debited amount will reflect the latest recalculation. See Ticket pricing and multi-level markup.
Read-only nature of the Orders API group
Section titled “Read-only nature of the Orders API group”On the partner integration tier, you cannot create or confirm orders directly via the Orders API group. The entire order lifecycle must be driven through the Bookings group. The Orders group is provided solely to inspect details of individual leg orders.
Partners also have no invoice read API, despite receiving events such as invoice.created, invoice.updated, invoice.published, and invoice.failed. Do not design an invoice event handler that depends on fetching invoice details through the partner API; see Webhooks.
Nine automated operators, four manual operators
Section titled “Nine automated operators, four manual operators”Among the 13 ferry operators connected to Ferry:
- 9 operators support automated issuance: Confirmation calls communicate with the operator engine and issue tickets when processing succeeds. Some operators can take 5–8 minutes; automated issuance does not mean an immediate response.
- 4 operators lack automated connector APIs: Binh An Ha Tien, Thanh Thoi, Hang Chau Tourist, and Saigon WaterGo. Bookings for these operators can be created, but confirming ticket issuance returns an unimplemented error. Partners should filter out these carriers or arrange manual booking with Ferry operations.
Re-downloading issued tickets
Section titled “Re-downloading issued tickets”When passengers lose their tickets or require re-delivery via email, call the ticket download endpoint (GET /bookings/{id}/tickets). This retrieves copies of previously issued ticket files without issuing new tickets or incurring charges.
Common errors and remedies
Section titled “Common errors and remedies”| Symptom | Probable cause | Recommended action | Retry safe |
|---|---|---|---|
| HTTP 422 reported as processing | 600-second lock currently held for this booking | Wait several minutes and poll booking status | Yes, after waiting |
| Cooldown error (exceeded 5 req/60s) | Sent repeated confirmation attempts in rapid succession | Back off and wait for the 60-second window to clear | Yes, after 60 seconds |
| Insufficient balance error | Partner account or an upstream parent lacks credit | Top up balance or request credit increase from parent | Yes, after funding |
| Unimplemented connector error | Voyage belongs to one of the 4 manual operators | Cancel booking and arrange ticketing via Ferry operations | Do not retry via API |
| Request hangs without responding | Operator engine connection slow (takes 5–8 minutes) | Do not abort; switch to periodic status polling | Do not issue new call |
| Booking failed but balance partially debited | Partial issuance failure on a multi-leg booking | Inspect individual leg orders and contact Ferry support | Do not create duplicate |
Where to look on Scalar
Section titled “Where to look on Scalar”To inspect exact request schemas and test calls directly, open the Scalar UI under the relevant tags:
| Capability | Scalar tag |
|---|---|
| Confirm ticket issuance and download tickets | Bookings |
| Inspect list and details of individual leg orders | Orders |
| View passenger details linked to tickets | Customers |
For instructions on configuring your API key in Scalar, see API Reference.
Read next
Section titled “Read next”| Page | Related topic |
|---|---|
| The booking flow | Booking lifecycle and payment timers |
| Payments and balances | Balance deduction mechanics and credit limits |
| Webhooks and events | Real-time event notifications for ticket issuance |
| Cancellation and refund | Ticket cancellation procedures and refund terms |
| Environments | Ticketing behavior differences in the UAT sandbox |