Skip to content

Ticket cancellation and refunds

10 min read

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.

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.

RequirementWhyWhere to read
A booking in the booked stateOnly a ticket that has been issued can be cancelledBooking: the life cycle of one booking
Knowing how many levels deep the organization chain runsThe number of levels decides the approval durationIntegration Surface Overview
Your organization still has enough balanceA cancellation request is rejected right away if there is not enough balance to pay the feeMoney: balance, credit limit and debt
A declared address for receiving eventsThis flow emits frequent events across the systemWebhooks
Understanding the three price figuresThe refunded amount is computed on the NET price from each level’s perspectiveTicket price and multi-level markup

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"
end
StepWhat your system doesWhat Ferry doesSuccess signalCommon failures
1Check the booking is in the booked state—Booking is in issued statusCreating a request for a booking that has no ticket
2Send the create-cancellation call with ticket and reasonBuild the whole organization chain, check balanceReceive a request in the processing stateBalance is insufficient, causing immediate rejection
3Save the cancellation request ID in your database—Request ID stored in your systemKeeping it only in memory and losing track
4Inform customer that multi-level review is pendingWait for each level to actCustomer understands multi-tier approval is requiredPromising a fixed refund deadline
5Track the overall state and each level’s stateUpdate individual level state after each reviewSee clearly which level is currently reviewingReading only the overall state and assuming the system is stuck
6When refunded, reconcile against transaction logRelease seats, release vouchers, credit child organizationsFind the matching refund transactionTelling customer refund is complete before money actually arrives
7If rejected, read the reason and follow up with customerWrite rejection reason onto requestConcrete reason available to explain to customerCreating a new request without resolving the rejection cause

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 approved
LabelMeaningWhat you do next
ProcessingThe request entered the approval chain and is not yet finishedTrack progress, take no further action
ApprovedOne or more levels approved, root has not yet approvedContinue tracking, check individual state to see who is next
RejectedAny level rejected the request, with a reasonRead reason, follow up with customer, ticket remains intact
RecalledCustomer withdrew the requestNo further operations possible, all actions blocked
RefundedRoot organization approved, seats and vouchers releasedReconcile against transaction log, notify customer once money arrives

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.

The approval chain walks back up, six steps

Section titled “The approval chain walks back up, six steps”
StepSystem progression
1Create 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.
2Each level approves from the bottom up. A parent level can only approve after its direct child has approved.
3When a level approves, only that level’s own state changes; the overall state remains processing.
4Root organization approves: overall state transitions to refunded, seats and vouchers are released.
5Any level rejects: the entire request is rejected, accompanied by a specific rejection reason.
6Customer 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.

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.

LegMechanismTime
Crediting child organizationSystem attempts to credit immediately upon root approvalInstant if execution succeeds
Failed crediting attemptBackground task sweeps pending items periodicallyEvery 15 minutes
Record stuck in-flightRecalled to the queue for retryAfter 10 minutes
Continued failuresSystem raises a financial alertAfter 3 consecutive failures
End-customer fundsRecorded under a dedicated refund transaction typeSimultaneously 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 prior to cancellationAfter cancellation completes
UnpaidCancelled
Partially paidCancelled — the system has no partial refund state
PaidRefunded
Paid with debtRefunded — 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.

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.

SymptomCauseWhat to doShould you retry
Request rejected immediately upon creationBalance insufficient to pay the fee (code 160105)Top up organization balance, then recreate requestNo — retrying without topping up will fail again
Overall state sits still for hoursApproval chain is waiting for a higher level to actInspect individual level states to see who is pending, then contact that entityNo — calling the API again does not accelerate approval
No operation found to approve requestPartners do not have approval permissions by system designHigher-level agencies will review and approve via their admin interfaceNot applicable
Request changes to recalled unexpectedlyCustomer recalled the request through another channelAccept this state; further actions are blockedNo
State shows refunded but funds have not arrivedCrediting task runs on a 15-minute sweep cycleWait out the cycle, verify via transaction logNo
Funds not received after multiple sweep cyclesCrediting failed repeatedly and an internal alert was raisedContact Ferry support with the cancellation request IDNo
No webhook events received at allFilter condition on the receiving address has invalid syntaxRemove or fix the filter condition, verify with a real actionNo
Two identical cancellation requests for one ticketCreate call was retried after encountering a timeoutContact Ferry to remove the duplicate recordNo

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 goalGroup in API reference
Data structure for creating cancellation requestsCancel Ticket Requests
Listing cancellation requests and filter parametersCancel Ticket Requests
Corresponding refund transactionTransactions
Booking state after cancellation completesBookings

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.

PageWhen to read
Booking: the life cycle of one bookingWhen you need to understand booking state changes while cancellation is pending
Money: balance, credit limit and debtWhen you need to reconcile refund transactions with scheduled crediting cycles
Ticket issuance and ordersWhen you need to cancel a booking with a partial ticketing failure
WebhooksWhen you need to subscribe to events and avoid filter syntax traps
Ticket price and multi-level markupWhen you need to understand refund calculations from each organization’s view
Integration Surface OverviewWhen you need to understand multi-tier approval chains
QuestionStatus
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.