Let's talk
data-modelling

We sent a stranger another customer's pickup time

WhatsApp confirmations to foreign customers were failing. The failures were the lucky cases: three messages with a UK customer's booking reached a stranger's phone.

·

You run a self-drive car rental business in Goa, and a good share of your customers fly in from abroad. Your booking confirmations go out by WhatsApp. One morning you notice the messaging service reporting an error on a New York number, and you ask why a customer in the United States is not getting his confirmation.

The error was the good news. The message to that customer had been addressed to an Indian mobile number that happened to share his last ten digits, and the service had refused it. When it did not refuse, the message was delivered. Looking back through the delivery records: three messages carrying a UK customer’s name, booking code, pickup time and pickup place had been delivered to one unrelated Indian number. Four more had been accepted for delivery to recipients nobody can identify.

Sized across the whole history of the booking site: of 15,862 bookings, 281 would have had their messages sent to a stranger, 406 would have got nothing and no warning, and five of those bookings were live with trips still ahead.

What was actually going on

The messaging code assumed every customer was Indian. It kept the last ten digits of whatever number it was given and put India’s dialling code in front. A UK mobile is twelve digits, so it lost its first two and became a perfectly plausible Indian one. A Polish number did the same. A number with fewer than ten digits, a common typing slip, was silently dropped with no record.

The numbers arrived that way because of the public booking form, which nobody had looked at from this angle. It asked for a phone number in a plain text box with no dialling code, no length check and no hint. Its country dropdown defaulted to a country and almost nobody changed it: 6,686 of the 7,359 bookings marked “United States” carried a plain ten-digit Indian mobile. So the country field could not be used to repair the number either. In four years, not one number had been entered with an international prefix. 921 numbers were in a shape that identifies no country at all.

Then, in September, the booking site’s own vendor changed the form and added a flag-and-prefix picker, which started saving numbers with a plus and a code where there had only ever been ten bare digits. That broke the customer’s own booking page, which checked the typed number against the stored one character for character. And the picker guessed the country from the browser’s language: an English browser got +1, and a browser sending no language at all got +30.

What we changed

The guess was replaced with a proper phone-number library. A number with an explicit plus is believed. Ten bare digits are Indian, by house rule, because that is what the counter staff type. A leading zero is a trunk prefix. Anything longer than ten digits carries its own country code. An invalid number now produces no message rather than a wrong one, which is the right direction to fail in.

Before deploying, the new rule was run against all 15,160 production bookings and compared with the old: 14,601 unchanged, 186 that had been going to a wrong Indian number now correctly addressed (79 UK, 35 US, 31 Russian, 16 Polish, and a long tail), 181 that had been silently rejected now reachable, and 43 that the old code had been guessing at now refused because the number is not valid anywhere.

On the booking site, the country dropdown and the duplicate WhatsApp field were switched off, the picker’s default was fixed so an English or language-less browser gets +91 while a German or French one still detects correctly, and the customer booking page now matches on the last ten digits so a customer with a + in their stored number can open their own booking again.

What it did not fix

The three messages that reached a stranger cannot be recalled. Nothing was ever read, as far as the delivery receipts show, but the person has a name, a booking code and a pickup time they should not have.

The messaging provider’s interface cannot list failures or history at all; every figure here comes from the delivery receipts we capture ourselves, which only began on 23 August, so 69 of the 1,258 tracked messages could not be matched to a booking. 123 stored numbers are unusable under either rule. And a valid number that belongs to somebody else is undetectable by any check: the only defence is showing the customer their own number back in the confirmation email, which was proposed and not yet built.

The mechanism, briefly

splitIndianNumber kept the last ten digits and prefixed 91; replaced with libphonenumber-js parsing that returns null on invalid input, applied at the sync boundary so the stored column keeps one shape. The vendor form’s visitorCountryCode() took the region from Accept-Language and fell through to a hard-coded default; two edits corrected the plain-English branch and the fallback.

Where this ends up

A confirmation that goes to the wrong person is worse than one that never goes, which is why the messaging in Sazinga Rentals refuses a number it cannot parse rather than guessing a country for it.

This came out of building Sazinga Rentals

Bookings, availability and the fleet standing behind them. The problem above is one we met while building it, and what we did about it is in the product.

If you run something like this, tell us how it works today and we will tell you what it would take to move.