Back to Blog

Ussd extending the example to a Community-Based Surveillance (CBS) use case

696a4edf0617153016f9e0d8

Below is a technical walkthrough of the Africa’s Talking USSD flow, using your Flask / Express examples, and then extending the example to a Community-Based Surveillance (CBS) use case.


1. USSD Session Lifecycle (Africa’s Talking)

When a user dials your USSD code (e.g. *384*123#):

  1. MNO receives the request

  2. Request is forwarded to Africa’s Talking

  3. Africa’s Talking sends an HTTP POST request to your /ussd endpoint

  4. Your application responds with:

    • CON → session continues
    • END → session terminates

Africa’s Talking does not store session state. Your application is responsible for interpreting the text field.


2. Incoming API Payload (What Your Server Receives)

Content-Type:

application/x-www-form-urlencoded

Payload fields:

FieldDescription
sessionIdUnique identifier for the USSD session
phoneNumberMSISDN of the user
networkCodeMobile network (e.g. Safaricom, Airtel)
serviceCodeYour assigned USSD short code
textUser input, *-concatenated

Example text evolution

User Actiontext value
Dial code""
Select 1"1"
Select 2"1*2"
Select 1 again"1*2*1"

3. Flask USSD Code – Walkthrough

Entry Point

@app.route("/ussd", methods=['POST'])
def ussd():

This endpoint must be publicly accessible and respond quickly (<5 seconds).


Reading Request Parameters

session_id   = request.values.get("sessionId", None)
serviceCode  = request.values.get("serviceCode", None)
phone_number = request.values.get("phoneNumber", None)
text         = request.values.get("text", "")
  • text == "" → first request in the session
  • Subsequent requests append input using *

First Menu (Session Start)

if text == '':
    response = "CON What would you want to check\n"
    response += "1. My Account\n"
    response += "2. My phone number"
  • CON keeps the session alive
  • Menu options must be plain text only
  • Avoid special characters

Handling User Input

elif text == '1':

User selected option 1 from the first menu.

elif text == '1*1':

User navigated:

Menu 1 → Option 1 → Option 1

This is how menu depth is inferred.


Ending the Session

response = "END Your phone number is " + phone_number
  • END immediately terminates the session
  • Any input after this is ignored

Important Rules

  • Always return HTTP 200
  • Response must start with CON or END
  • HTTP 40X errors terminate the session automatically

4. Express (Node.js) Version – Same Logic

Your Express example follows the same principles:

app.post('/ussd', (req, res) => {

Reading payload:

const { sessionId, serviceCode, phoneNumber, text } = req.body;

Response headers:

res.set('Content-Type', 'text/plain');

Africa’s Talking requires plain text responses.


5. Adding CBS (Community-Based Surveillance) Context

Now let’s adapt this USSD flow for a CBS reporting use case.

CBS Goal

Allow community members to:

  • Report unusual illness
  • Report deaths
  • Report animal health events
  • Submit reports without internet access

6. CBS USSD Menu Flow (Design)

Menu Structure

*384*123#
│
├── 1. Report Human Illness
│   ├── 1. Fever
│   ├── 2. Diarrhea
│   └── 3. Unknown illness
│
├── 2. Report Death
│   ├── 1. Adult
│   └── 2. Child
│
└── 3. Animal Health Event

7. CBS USSD Example (Flask)

@app.route("/ussd", methods=['POST'])
def ussd():
    text = request.values.get("text", "")
    phone_number = request.values.get("phoneNumber")

    if text == "":
        response = "CON CBS Reporting\n"
        response += "1. Report illness\n"
        response += "2. Report death\n"
        response += "3. Animal health event"

    elif text == "1":
        response = "CON Select illness type\n"
        response += "1. Fever\n"
        response += "2. Diarrhea\n"
        response += "3. Unknown illness"

    elif text == "1*1":
        response = "END Thank you. Fever case reported.\nA health officer will follow up."

    elif text == "1*2":
        response = "END Thank you. Diarrhea case reported.\nA health officer will follow up."

    elif text == "2":
        response = "CON Report death\n"
        response += "1. Adult\n"
        response += "2. Child"

    elif text == "2*1":
        response = "END Adult death reported. Surveillance team notified."

    elif text == "2*2":
        response = "END Child death reported. Surveillance team notified."

    else:
        response = "END Invalid choice"

    return response

8. CBS Backend Integration (After USSD)

In production, instead of static messages, you would:

  • Store reports in a database

  • Trigger alerts to:

    • Sub-county disease surveillance officers
    • CHVs / CHEWs
  • Integrate with:

    • DHIS2
    • IDSR systems
    • SMS follow-ups

9. Testing with Africa’s Talking Simulator

Steps:

  1. Register at Africa’s Talking

  2. Go to Sandbox

  3. Create a USSD service

  4. Assign a service code

  5. Set callback URL to:

    https://your-ngrok-link
  6. Use the USSD Simulator:

    https://developers.africastalking.com/simulator

10. Key CBS + USSD Best Practices

  • Keep menus short and clear
  • Avoid deep nesting (>3 levels)
  • Use local language if possible
  • Respond within <5 seconds
  • Always acknowledge reports
  • Provide feedback to build community trust

Conclusion

Africa’s Talking USSD provides a low-level, session-driven interface ideal for Community-Based Surveillance systems. By interpreting the text field correctly and managing session flow using CON and END, developers can build reliable, inclusive, and scalable CBS reporting tools that work on any mobile phone, even without internet access.