Skip to content
luftflow

Move a USSD service without changing the short code

October 15, 2026

At twenty to seven on a Tuesday morning, the person who owns Mwezi Bank's USSD line opens the aggregator's portal, finds the field marked Callback URL, and pastes in a new address. Mwezi is not a real bank; its menu is the one we use for every example, and it will stand in here for the service you run. By lunchtime several thousand people will have dialled *384#. None of them will notice anything.

That is the whole switch. Everything else in this piece is the four weeks that make that morning boring, because boring is the only acceptable way for a number that thousands of people dial to change what answers it.

What actually changes

Somewhere at the aggregator or operator there is one setting that says where each keypress goes. It is the only thing that moves.

The short code stays, with its rental and its agreement. The aggregator stays, billed separately as before. The bank's APIs stay at the same URLs; the flow calls them instead of the old handler. What moves is the thing nobody was looking after: the sessions, and with them the retries, the versions and the count of who dropped where.

Nothing is installed on the bank's side. If its risk team needs sessions to stay in the country, the runtime can run in the bank's own data centre, and even then the aggregator sees only a URL.

Week 1: the handler is the specification

Mwezi's menu arrives the way most do: a folder of screenshots from 2023, a Python file nobody has opened since, and the person who wrote it on a call for an hour. That is enough. The handler is the specification: not what the document says the menu does, but what it actually does when a caller presses 4.

So the rebuild keeps the odd corners and writes each one down. By the end of the first day the list looks like this.

Two of the four are kept on purpose, because thousands of callers have learned them and a move is not the moment to teach them something new. One is a bug nobody knew about. One is a question for the product team, who did not know the loan menu had never once been shown. This is the most useful hour of the whole move, and it has nothing to do with software.

The same afternoon the team walks the rebuilt menu on a phone in the browser. Then the paths they are most afraid of become saved scenarios. The first one recorded is never the happy path.

Each scenario dials the flow against the bank's mocked APIs, with the balance lookup mocked slow on purpose. From now on a publish stops if any of them fails, and that rule outlives the move.

Week 2: a real handset on the test code

Mwezi's aggregator gives it a test short code. Staging gets its own callback address on it, with its own keys and secrets, and someone dials it from a real phone on the real network. This is where the network's own habits show up, and they are not the same twice:

Africa's Talking   sessionId, phoneNumber, serviceCode, text
MTN                sessionid, msisdn, input, isnewrequest
Zamtel             TransId, MSISDN, USSDString, RequestType

One sends the whole conversation so far. One sends only the last key. One resends the short code in front of every key. The flow does not care, because the engine reads each gateway the way it actually asks, but the team wants to have seen it work on the network its callers are on before anyone else does.

Weeks 2 and 3: answer beside the old service

This is the part that makes the switch safe, and the part nobody else offers.

The flow runs beside the live service on real calls. Every caller still gets the old service's reply, untouched. luftflow's reply is produced in parallel, kept for a week, and compared screen by screen, with the ones that differ at the top.

Calls that would change anything, a transfer or a registration, are refused during the rehearsal unless the team names them safe. Nobody is charged twice and nobody is registered twice while the two services run side by side.

Then the team reads the differences together. The first morning there are a few dozen. Most are the rebuild's fault and are fixed by lunch. A handful are the old service's: a screen that said "Enter amount" where the rebuild said "Enter the amount to send", and three callers a day who typed a letter into the PIN field and were sent somewhere nobody had mapped. By the end of the week the two agree on every path that matters, and the team has seen it on a week of real traffic rather than taken anyone's word for it.

Week 3: rehearse the release

Publish, roll back, and publish again on staging, so that release day is a repeat of something the team has already done three times. Mwezi wants a second person to approve a version before it goes live, so that is switched on now and rehearsed with the rest.

Week 4: twenty to seven

The live callback URL moves on a quiet morning, which is where this piece started. Callers mid-session on the old service finish there. New callers arrive on luftflow. The traffic page counts them by screen from the first minute, so if something is wrong it shows in the drop-off chart within the hour, not in a complaint the next day.

That morning the traffic page is boring. That is the point.

Going back is one paste

The old callback URL is still Mwezi's. Paste it back at the aggregator, and the next caller is on the old service. Nothing was installed, so nothing is uninstalled. The old service stays on standby for as long as the team likes; most switch it off a few weeks later, once the traffic page has been boring for long enough.

What to have ready

  • Whoever owns the callback URL today, on one call.
  • The menu in any form: YAML, screenshots, or the code to dial.
  • A test endpoint for the APIs, or permission to stub them.
  • The aggregator's test short code, if there is one.

That is the whole list. If you send us the menu first, we will rebuild a path of it and send you a link to dial it on your own phone, before anyone decides anything.