Jackson Kojo Osei
statusoperationalaccra--:--:--regiongh · uk
operational

USSD Runtime

Run the USSD menus you design in USSD Flow Studio, fast enough for a real phone session.

What it does

An open-source Go service that runs USSD menu flows on Africa's Talking, Hubtel, Nalo and Arkesel. It runs USSD Flow Studio exports unchanged, and adds typed answers with validation, variables in prompts, and signed calls to your backend with a hard time limit. Sessions live in Redis, so several instances can share the work.

The problem

USSD still reaches people without smartphones or data, but every network gives you only a few seconds per reply, and every gateway speaks a different format. Slow servers mean dropped sessions and lost customers.

What you get

USSD simulator showing a student's name and outstanding fees, fetched from the school's backend
The caller typed a student ID; the runtime asked the school's backend and filled in the name and balance.
USSD simulator refusing an amount over the GHS 5,000 limit and asking again
Typed answers are checked first. An amount over the limit is refused before it reaches the backend.
USSD simulator showing a payment request reference at the end of the session, with response times for each step
The end of the flow. Every step but the backend lookup answered in under a millisecond.
How USSD Runtime works: a caller dials, the gateway posts to the runtime, which loads the session from Redis, runs the flow, calls your backend if needed and replies
One service between the gateway and your backend.

How to use it

Design your menu in USSD Flow Studio (or write the JSON), run it with the runtime, and point your gateway's callback URL at it. Add input screens and actions when you need typed answers or data from your own system.

1. Run it with the simulator

git clone https://github.com/Jacksonoseikojo1122/ussd-runtime.git
cd ussd-runtime
FLOW_FILE=flows/demo-wallet.json SIMULATOR=on go run ./cmd/ussd-runtime
# open http://localhost:8080 and press Dial

2. Point your gateway at it

Africa's Talking  https://<your-host>/ussd/africastalking?token=<GATEWAY_TOKEN>
Hubtel            https://<your-host>/ussd/hubtel?token=<GATEWAY_TOKEN>
Nalo              https://<your-host>/ussd/nalo?token=<GATEWAY_TOKEN>
Arkesel           https://<your-host>/ussd/arkesel?token=<GATEWAY_TOKEN>

3. Ask for a typed answer and call your backend

"pay_student": {
  "prompt": "Enter the student ID", "kind": "input",
  "input": { "name": "student_id", "type": "digits", "min_length": 4, "next": "pay_lookup" }
},
"pay_lookup": {
  "prompt": "Pay for {{student_name}}?\nOutstanding: GHS {{balance}}", "kind": "menu",
  "action": { "url": "$BACKEND_URL/ussd/student", "timeout_ms": 2000, "can_go_to": ["not_found"] },
  "options": [{ "key": "1", "label": "Yes", "next": "pay_amount" }]
}

4. Answer the action from your backend (Node.js)

app.post("/ussd/student", express.raw({ type: "application/json" }), (req, res) => {
  if (!validSignature(req)) return res.status(401).end(); // X-USSD-Signature, see docs/actions.md
  const { vars } = JSON.parse(req.body.toString());
  const s = findStudent(vars.student_id);
  if (!s) return res.json({ next: "not_found" });
  res.json({ vars: { student_name: s.name, balance: s.balance } });
});

Documentation

  • Getting started Run the demo, your Flow Studio export, and a flow with a backend
  • Flow format Screens, input types, variables, actions and validation
  • Actions The backend contract and signature checks in Node, Python, PHP and Go
  • Gateways Africa's Talking, Hubtel, Nalo and Arkesel formats, with sources
  • Operations Load-test results, Redis, Docker, Kubernetes, metrics and alerts
  • OpenAPI spec Every endpoint, plus the action webhook

Features

  • Adapters for Africa's Talking, Hubtel, Nalo and Arkesel, built from each provider's documentation
  • Runs USSD Flow Studio exports unchanged: the Studio's engine tests run against it in CI
  • Input screens for text, digits, amounts and Ghana phone numbers, checked before your backend sees them
  • Signed backend calls with a 2-second budget and an allow-list of screens the backend can send callers to
  • Sessions in Redis (several instances) or memory (one), with a phone simulator for testing
  • Prometheus metrics and a load-test tool with published results

What building it covers

  • Go
  • Low-latency services
  • Load testing
  • Session state at scale

Stack

  • Go
  • Redis
  • Prometheus
  • Docker
  • Kubernetes

Questions

Which USSD gateways does it support?

Africa's Talking, Hubtel, Nalo and Arkesel. Each has its own endpoint, /ussd/africastalking, /ussd/hubtel, /ussd/nalo and /ussd/arkesel, and the runtime answers in that gateway's format.

Can I run a flow I designed in USSD Flow Studio?

Yes. Export the flow as JSON and set FLOW_FILE to it. USSD Flow Studio's own engine tests run against the runtime in CI, so callers get the same replies the Studio's simulator shows.

How do I ask the caller to type something, like an amount or a phone number?

Use an input screen with type text, digits, amount or phone_gh. The answer is checked (for example an amount between GHS 1 and GHS 5,000), stored as a variable and can be shown in later prompts as {{amount}}.

How does it get data from my system?

Add an action to a screen. When a caller reaches it, the runtime POSTs the session to your URL, signed with HMAC-SHA256, and waits up to 2 seconds. Your backend returns variables to show, or one of the screens you allowed it to send the caller to.

How fast is it?

On a laptop, with 50 callers going through a payment flow that calls a backend twice, it handled 14,000 to 19,000 requests a second with a p99 of about 10 ms. CI runs the same test with Redis on every change.

Do I need Redis?

Only if you run more than one instance. Without REDIS_URL, sessions are kept in memory.

Is it free?

Yes. It's open source under the MIT licence. You still need a USSD code from a gateway such as Africa's Talking, Hubtel, Nalo or Arkesel.

All projects