An open gateway and SDK for the feature-phone web
OpenUSSD is a self-hostable gateway that receives USSD sessions from a mobile network or aggregator, keeps the session state, and hands each screen to your web service as a signed HTTP webhook. A Go SDK builds the menus and keeps every screen inside what a handset can show.
v0.1, working, not production. Interfaces will change before 1.0. AGPL-3.0-or-later.
A real session
From cmd/ussdsim, the terminal handset that ships with the gateway. A USSD screen holds 182 characters in the GSM 7-bit alphabet, or only 70 once any character outside it appears: an accent, a non-Latin script, an emoji. The SDK measures every screen in the encoding its text forces.
OpenUSSD Fediverse 1. Public timeline 2. About 0. Quit
GSM-7 · 93 / 182Dial *384*1234#. The gateway routes the shortcode to the Fediverse tenant. Latest posts 1. Kiambu Farmers Coop (now) 2. Shule ya Msingi (3m) 3. Ward Office (9m) 0. Quit
GSM-7 · 93 / 182Display names lose their emoji, so the list stays GSM-7 and more posts fit. Shule ya Msingi (1/2) Habari za asubuhi! Mkutano wa wazazi utafanyika Jumamosi saa nne asubuhi katika ukumbi wa shule. Tafadhali fika mapema 1. Next 0. Back
GSM-7 · 156 / 182A Swahili post ends in an emoji. The text stays GSM-7 and gets the full 182. Shule ya Msingi (2/2) 🙏 2. Prev 0. Back
UCS-2 · 40 / 70Only the screen holding the emoji pays for UCS-2, where the budget is 70.
The posts are demo data served by a local test instance. Every screen and budget figure is the gateway's own output.
Try it
Needs Docker for the gateway and Go for the handset. No telco account, sandbox or tunnel.
git clone https://github.com/davidrukahu/openussd
cd openussd
export FEDIVERSE_WEBHOOK_SECRET=$(openssl rand -hex 32)
docker compose up --build
# in a second terminal
go run ./cmd/ussdsim -shortcode '*384*1234#'To dial from a browser handset instead, enable the Africa's Talking adapter and point a free sandbox USSD channel at the gateway. See getting USSD access in practice.
How it fits together
handset ── *384*1234# ──▶ network or aggregator
│ native callback format
▼
┌──────── OpenUSSD gateway ─────────┐
│ telco adapter → canonical event │
│ session store (memory | Redis) │
│ tenant router (shortcode, menu) │
└───────────────────────────────────┘
│ HMAC-signed webhook
▼
your service, any language
(Go SDK today, TypeScript planned)Adding a network means one adapter package that turns its callback into a canonical event and renders the reply back. Applications never see a telco's wire format. Design notes live in docs/rfcs and the full tenant contract in the webhook protocol.
Status
Working in v0.1
- Canonical session events and the adapter interface (RFC-0001)
- Africa's Talking adapter, contract tests including captures from the live sandbox (#7)
- Session store: in-memory and Redis, 180 s idle expiry
- Tenant routing with HMAC-signed webhooks (protocol)
- Go SDK: typed screens, state, i18n, encoding-aware screen budget (pkg.go.dev)
- Mastodon public timeline over USSD, paginated
Not yet
- SMS
- A second real network
- TypeScript SDK
- Fediverse write paths and identity binding (#9)
- Postgres audit store
- Independent security audit
Known limitations
- Webhooks carry no replay nonce. The signature's 5-minute window bounds replay but does not prevent it, so tenants should deduplicate.
- The simulator adapter authenticates nothing. It is the demo; never expose it.
- Two callbacks for one session are not yet serialised; a retried callback can reach the tenant twice.
- No prebuilt binaries. Build from source or use Docker Compose.
Work is planned in GitHub milestones and deliberate deferrals are recorded in TODOS.md.
Why
In Sub-Saharan Africa, 60% of mobile internet subscribers still use a feature phone or a 3G smartphone (GSMA, 2025), and about half of all mobile connections there are not smartphones (GSMA, 2024). For those users a USSD shortcode is often the only way to reach a service.
A service that wants that reach today writes against an aggregator's proprietary API, country by country. OpenUSSD puts an open, self-hosted layer in between, so the service is written once and the network can change underneath it.
Related projects
Others have worked on parts of this. How OpenUSSD differs: it is a gateway with a telco-adapter interface, not a single-aggregator SDK. It speaks one signed webhook protocol that any language can implement, and ships with a terminal simulator so it can be tried without a telco.
Project
- Maintainer
- David W, Nairobi, Kenya
- Licence
- AGPL-3.0-or-later for the code, CC BY-SA 4.0 for the documentation
- Funding
- Unfunded. An application to the NLnet NGI Zero Commons Fund is in second-round review.
- Contributing
- Guide and code of conduct. If you have integrated against an African network, #11 wants to hear what surprised you.