Investigation deep dive
A booking error nobody reported, five explanations that were wrong, and the test that found the real one: another booking site holding our customers' passport numbers before they'd paid.
The short version
When a customer books a ferry on our site, we first place a provisional booking with the ferry operator: a hold that reserves the places and checks the passenger details, confirmed once the customer pays. For one operator, on routes to and from one country, some of those holds came back rejected - the passenger's document number was "already in use" on that route.
Nobody had flagged it. It only showed up in the data.
Ruling things out
When an outside system rejects your request, suspect your own request first. Each idea got the cheapest check that could rule it in or out.
The clue
One customer hit the error, waited three minutes, and tried again with the same passengers on the same route. The second attempt worked.
So the passport wasn't the problem. Something had been holding it, and the hold had gone away, and it wasn't us: we'd only sent one request. Someone else was placing holds with our customers' passport numbers.
The obvious someone else is another booking site. Customers compare prices. If a rival site places a hold as soon as passenger details are entered, before payment, a customer who checks prices there and then comes to us gets rejected by the operator.
The test
That idea could be tested directly. I went through a booking on another site, on one of the affected routes: entered passenger details, stopped before paying, then tried the same passengers on the same route with us.
A customer compares prices, enters their details on two sites, and finds they can book on neither.
One passport, two booking sites, one operator. Follow the request, the hold that blocks it, and the retry that exposed the cause.
The customer enters passport details on the other site. It reserves places with the operator before any payment.
What to do about it
The hold lives in the operator's system, so the real fix has to be theirs. I wrote up two options, and either solves it:
On our side, a cheap stopgap is to tell the customer what's happening: these places may be held elsewhere, try again in a few minutes. It won't save everyone: in our data, almost none of the affected customers got through within the hour. Still, it beats an error and a lost customer.
What happened next: neither one. The operator didn't adopt either fix, and the retry message never shipped.
Wrapping up
A booking error that looked like bad data turned out to be another site's holds. The proof came from following the one customer whose retry worked, then booking through the other site myself.
Rule out your own system first, with the cheapest check that settles each idea. And when a pattern only makes sense from the customer's side, go and be the customer.
Glossary