ussd-router: an Express-style state router for USSD apps, with adapters for Africa's Talking and Qrios, TypeScript-first, actual test coverage, and an honest scope — I didn't fake support for aggregators whose API docs I couldn't verify (VAS2Nets, BergenHorn), rather than ship code that compiles but silently does the wrong thing.
# ussd-router
Express-style state router for USSD applications, with adapters for
**Africa's Talking** and **Qrios** — the two aggregators whose developer
docs were actually verifiable at the time this package was written.
```ts
import { UssdRouter, africasTalkingAdapter } from "ussd-router-plus";
import express from "express";
const router = new UssdRouter();
router.state("root", (ctx) => {
ctx.reply("Welcome\n1. Check balance\n2. Buy airtime");
ctx.next({ "1": "balance", "2": "buyAirtime" });
});
router.state("balance", async (ctx) => {
const bal = await getBalance(ctx.phoneNumber);
ctx.end(`Your balance is NGN ${bal}`);
});
router.state("buyAirtime", (ctx) => {
ctx.reply("Enter amount:");
ctx.next({ "*": "buyAirtime.amount" });
});
router.state("buyAirtime.amount", (ctx) => {
ctx.end(`You bought NGN ${ctx.input} airtime`);
});
const app = express();
app.use(express.json());
app.post("/ussd", async (req, res) => {
const normalized = africasTalkingAdapter.parseRequest(req.body);
const outcome = await router.handle(normalized);
res.type("text/plain").send(africasTalkingAdapter.formatResponse(outcome, normalized));
});
app.listen(3000);
```
## Why this exists, given `ussd-builder` and `ussd-router` (yes, same name) already exist
They do — this isn't an unclaimed idea. Research before building this turned
up at least four prior npm packages doing essentially the same thing
(`ussd-builder`, the original `ussd-router`, `ussd-menu-builder`, `Ananse`),
none of them released in the last 12+ months. The pattern itself (a
state-machine router for USSD menus) is well-trodden — this package doesn't
claim to have invented something new there.
What none of those four cover: **Qrios**, whose protocol is structurally
different from the Africa's Talking-style plain-text `CON`/`END` model every
existing package is built around. Qrios sends structured JSON across four
separate webhook endpoints (`new`/`continue`/`close`/`abort`) instead of one
endpoint with an accumulate …