- Developers
- Ticket cancellation and refunds
Ticket cancellation and refunds
The ticket cancellation and refund flow handles scenarios where passengers do not proceed with their trip after tickets have been successfully issued. Integrated partners interact via the API only to create cancellation requests and monitor their status; approvals and rejections remain the responsibility of parent organizations and the root organization.
What this flow solves
Section titled “What this flow solves”Your customer already has a ticket but is not travelling any more. The partner surface in this flow has only two jobs: create a cancellation request, and view the list of cancellation requests your organization owns. You cannot approve, cannot reject, and cannot recall. Those three actions sit at the internal tier of Ferry and of the organizations above you.
Design consequence: do not draw a ticket-cancellation approval screen in your product. What you need to build is a tracking screen: which level the request is sitting at, how far it has been approved, and whether the refund has arrived.
Before you start
Section titled “Before you start”| Requirement | Why | Where to read |
|---|---|---|
| A booking in the booked state | Only a ticket that has been issued can be cancelled | Booking: the life cycle of one booking |
| Knowing how many levels deep the organization chain runs | The number of levels decides the approval duration | Integration Surface Overview |
| Your organization still has enough balance | A cancellation request is rejected right away if there is not enough balance to pay the fee | Money: balance, credit limit and debt |
| A declared address for receiving events | This flow emits frequent events across the system | Webhooks |
| Understanding the three price figures | The refunded amount is computed on the NET price from each level’s perspective | Ticket price and multi-level markup |
Flow diagram
Section titled “Flow diagram”The diagram below shows the approval chain walking back up from you to the root organization, with the rejection branch alongside it:
sequenceDiagram autonumber participant P as "Your system" participant F as "Ferry API" participant U as "Parent agency" participant R as "Root organization" participant C as "Crediting task" participant W as "Webhook address"
P->>F: "Send cancellation request with ticket and reason" F->>F: "Build the whole organization chain" F->>F: "Check your balance"
alt Not enough money to pay the fee F-->>P: "Request is rejected at once, never enters approval chain" else Enough money F-->>P: "Request is in processing state" F--)W: "Cancellation request created event"
U->>F: "Direct parent approves" Note over F: "Parent level state changes,<br/>overall state remains processing"
alt Any level rejects U->>F: "Reject with a reason" F--)W: "Rejected event" F-->>P: "Whole request is rejected, ticket stays intact" else Approved all the way to root R->>F: "Root organization approves" F->>F: "Release seats and vouchers" F->>C: "Credit each child organization in chain" C-->>F: "Crediting finished or waiting for next sweep" F--)W: "Refunded event" F-->>P: "Overall state moves to refunded" end end
loop Track approval progress P->>F: "Check cancellation request status" F-->>P: "Overall state and each level state" endStep-by-step
Section titled “Step-by-step”| Step | What your system does | What Ferry does | Success signal | Common failures |
|---|---|---|---|---|
| 1 | Check the booking is in the booked state | — | Booking is in issued status | Creating a request for a booking that has no ticket |
| 2 | Send the create-cancellation call with ticket and reason | Build the whole organization chain, check balance | Receive a request in the processing state | Balance is insufficient, causing immediate rejection |
| 3 | Save the cancellation request ID in your database | — | Request ID stored in your system | Keeping it only in memory and losing track |
| 4 | Inform customer that multi-level review is pending | Wait for each level to act | Customer understands multi-tier approval is required | Promising a fixed refund deadline |
| 5 | Track the overall state and each level’s state | Update individual level state after each review | See clearly which level is currently reviewing | Reading only the overall state and assuming the system is stuck |
| 6 | When refunded, reconcile against transaction log | Release seats, release vouchers, credit child organizations | Find the matching refund transaction | Telling customer refund is complete before money actually arrives |
| 7 | If rejected, read the reason and follow up with customer | Write rejection reason onto request | Concrete reason available to explain to customer | Creating a new request without resolving the rejection cause |
States
Section titled “States”Life cycle diagram of one cancellation request
Section titled “Life cycle diagram of one cancellation request”The diagram below shows the state transition lifecycle of a cancellation request:
stateDiagram-v2 direction LR state "Processing" as processing state "Approved" as accept state "Rejected" as reject state "Recalled" as recall state "Refunded" as refunded
[*] --> processing : Request created successfully with enough balance processing --> accept : One level in the chain approves accept --> processing : Levels above have not approved yet accept --> refunded : Root organization approves, system releases seats processing --> reject : Any level rejects processing --> recall : Customer recalls request accept --> recall : Customer recalls before root has approvedThe list of states
Section titled “The list of states”| Label | Meaning | What you do next |
|---|---|---|
| Processing | The request entered the approval chain and is not yet finished | Track progress, take no further action |
| Approved | One or more levels approved, root has not yet approved | Continue tracking, check individual state to see who is next |
| Rejected | Any level rejected the request, with a reason | Read reason, follow up with customer, ticket remains intact |
| Recalled | Customer withdrew the request | No further operations possible, all actions blocked |
| Refunded | Root organization approved, seats and vouchers released | Reconcile against transaction log, notify customer once money arrives |
Two tiers of state, you must read both
Section titled “Two tiers of state, you must read both”Beyond the overall state in the table above, each organization in the chain also carries its own state inside the related organizations list of the request.
This is where many partners misunderstand the system: when your direct parent has approved but a higher level has not, the overall state still shows as processing. Looking only at the overall state shows no movement, while in fact the request has moved up one level.
To know who you are waiting on, read the request detail and look at each level’s individual state. Present this status clearly on your customer support screens.
One blocking rule: while a request is in the Recalled state, all other actions are blocked completely. No approving, no rejecting, and no editing.
Guarantees and limits
Section titled “Guarantees and limits”The approval chain walks back up, six steps
Section titled “The approval chain walks back up, six steps”| Step | System progression |
|---|---|
| 1 | Create request: system builds the entire organization chain from you up to the root, each with its own state. System checks balance — insufficient balance rejects the request immediately. |
| 2 | Each level approves from the bottom up. A parent level can only approve after its direct child has approved. |
| 3 | When a level approves, only that level’s own state changes; the overall state remains processing. |
| 4 | Root organization approves: overall state transitions to refunded, seats and vouchers are released. |
| 5 | Any level rejects: the entire request is rejected, accompanied by a specific rejection reason. |
| 6 | Customer recalls: state changes to recalled. Recall is no longer permitted once the root organization has approved. |
Once you create the request, you cannot intervene further in the approval process. The time to completion depends on the number of organization levels above you and how quickly each level acts. The system has no automatic deadline for approval steps and no auto-approval on timeout.
Checking API keys when creating requests
Section titled “Checking API keys when creating requests”Both the create-cancellation and list-cancellations calls check the validity of the API key, without role-based internal authorization. Any valid API key belonging to your organization can create a cancellation request.
From a security standpoint, if your key is leaked, whoever holds the key can create cancellation requests for any ticket owned by your organization. Build your own permission checks within your system before invoking this API.
Refund mechanics and processing time
Section titled “Refund mechanics and processing time”| Leg | Mechanism | Time |
|---|---|---|
| Crediting child organization | System attempts to credit immediately upon root approval | Instant if execution succeeds |
| Failed crediting attempt | Background task sweeps pending items periodically | Every 15 minutes |
| Record stuck in-flight | Recalled to the queue for retry | After 10 minutes |
| Continued failures | System raises a financial alert | After 3 consecutive failures |
| End-customer funds | Recorded under a dedicated refund transaction type | Simultaneously with root organization approval |
The crediting operation is idempotent, locked by the cancellation request ID. Re-running the background task will not credit funds twice, so you do not need to build deduplication for this incoming refund amount.
However, the create-cancellation call itself lacks an idempotency key. On timeout, query your cancellation list before sending a new create call.
Payment state mapping on cancellation
Section titled “Payment state mapping on cancellation”| Payment state prior to cancellation | After cancellation completes |
|---|---|
| Unpaid | Cancelled |
| Partially paid | Cancelled — the system has no partial refund state |
| Paid | Refunded |
| Paid with debt | Refunded — cash portion refunded, debt portion cleared |
Note that the system does not support a “partial refund” state. A partially paid booking that is cancelled carries the cancelled state, and previously collected funds are reconciled through transaction logs rather than displayed on the booking state.
Webhook events worth subscribing to
Section titled “Webhook events worth subscribing to”To avoid constant polling, subscribe to the following events:
- Cancellation request created
- Cancellation request created by child organization
- Cancellation request updated
- Cancellation request approved
- Cancellation request rejected
- Cancellation request approved with refund
- Cancellation request refunded
- Cancellation request recalled
- Ticket released
Details on configuring receiving endpoints and operational considerations are available in Webhooks.
Common failures and how to handle them
Section titled “Common failures and how to handle them”| Symptom | Cause | What to do | Should you retry |
|---|---|---|---|
| Request rejected immediately upon creation | Balance insufficient to pay the fee (code 160105) | Top up organization balance, then recreate request | No — retrying without topping up will fail again |
| Overall state sits still for hours | Approval chain is waiting for a higher level to act | Inspect individual level states to see who is pending, then contact that entity | No — calling the API again does not accelerate approval |
| No operation found to approve request | Partners do not have approval permissions by system design | Higher-level agencies will review and approve via their admin interface | Not applicable |
| Request changes to recalled unexpectedly | Customer recalled the request through another channel | Accept this state; further actions are blocked | No |
| State shows refunded but funds have not arrived | Crediting task runs on a 15-minute sweep cycle | Wait out the cycle, verify via transaction log | No |
| Funds not received after multiple sweep cycles | Crediting failed repeatedly and an internal alert was raised | Contact Ferry support with the cancellation request ID | No |
| No webhook events received at all | Filter condition on the receiving address has invalid syntax | Remove or fix the filter condition, verify with a real action | No |
| Two identical cancellation requests for one ticket | Create call was retried after encountering a timeout | Contact Ferry to remove the duplicate record | No |
Where to look in Scalar
Section titled “Where to look in Scalar”This page corresponds to the Cancel Ticket Requests group on the API reference at https://api.ferry.vn/docs, along with two supporting groups for data reconciliation:
| Inspection goal | Group in API reference |
|---|---|
| Data structure for creating cancellation requests | Cancel Ticket Requests |
| Listing cancellation requests and filter parameters | Cancel Ticket Requests |
| Corresponding refund transaction | Transactions |
| Booking state after cancellation completes | Bookings |
In the API reference, the Cancel Ticket Requests group only displays operations partners are authorized to call (create and list). Internal operations such as approve, reject, or recall do not appear for external partners.
Read next
Section titled “Read next”| Page | When to read |
|---|---|
| Booking: the life cycle of one booking | When you need to understand booking state changes while cancellation is pending |
| Money: balance, credit limit and debt | When you need to reconcile refund transactions with scheduled crediting cycles |
| Ticket issuance and orders | When you need to cancel a booking with a partial ticketing failure |
| Webhooks | When you need to subscribe to events and avoid filter syntax traps |
| Ticket price and multi-level markup | When you need to understand refund calculations from each organization’s view |
| Integration Surface Overview | When you need to understand multi-tier approval chains |
Open questions
Section titled “Open questions”| Question | Status |
|---|---|
| Is there a guaranteed turnaround time for each approval step? | The system enforces no automated deadlines per approval step; actual turnaround depends on operational workflows of each party. |
| How is the cancellation fee computed and who decides it? | Governed by individual ferry operator policies and agency tier rules; currently no public API exists for querying fee schedules. |
| Through which channel do customers recall requests? | The channel has not been established; partner API keys cannot recall requests on a customer’s behalf. |