Customer USSD Guide
The customer USSD channel gives Emali 2.0 customers a fast, low-data way to access wallet services from any phone. Customer USSD runs on *123#, starts with language selection, remembers the customer's language preference, and asks for a PIN before sensitive actions.
Tip: Use USSD when the customer is on a feature phone, has poor data connectivity, or needs a quick assisted flow at an outlet or call center.
At a glance
| Item | Value |
|---|---|
| Dial code | *123# |
| Audience | Customers |
| Languages | English and siSwati |
| Session identity | MSISDN from the telco session |
| Sensitive-action auth | Customer transaction PIN |
| Session timeout | 5 minutes of inactivity |
| Test gateway | https://api.test.emali2.damplabs.com/api/v1/ussd/sessions |
| Local gateway | http://localhost:8805/ussd_gateway/api/v1/ussd/sessions |
Customer menu
| Option | Action | What the customer does |
|---|---|---|
| 1 | Check Balance | View wallet balance after PIN verification |
| 2 | Send Money | Enter recipient, amount, PIN, and confirm |
| 3 | Pay Bills | Pick a recent/popular paybill when available, or enter the paybill manually |
| 4 | Buy Airtime | Buy airtime for self after PIN confirmation |
| 5 | Mini Statement | View the latest 5 transactions |
| 6 | My Account | Change PIN, change language, view account info, limits, notifications |
| 7 | Cash Out | Enter agent till, enter amount, enter PIN, confirm, and receive a withdrawal code |
| 8 | Requests & Support | Review pending customer approvals and support-driven actions |
Session lifecycle
- Customer dials
*123#. - The first screen asks for language:
1. Englishor2. siSwati. - The selected language is saved and reused in the rest of the session.
- The customer enters a PIN before sensitive views such as balance, mini statement, and approval actions.
- Transaction flows such as send money, pay bills, airtime, and cash out request the transaction PIN before confirmation.
Live menu examples
English
Language / Lulwimi
1. English
2. siSwati
Current / Manje: English
Select option / Khetsa:
EMALI MOBILE MONEY
1. Check Balance
2. Send Money
3. Pay Bills
4. Buy Airtime
5. Mini Statement
6. My Account
7. Cash Out
8. Requests & Support
siSwati
Language / Lulwimi
1. English
2. siSwati
Current / Manje: siSwati
Select option / Khetsa:
EMALI MOBILE MONEY
1. Bona Imali
2. Thumela Imali
3. Khokha Timbilisi
4. Thenga I-airtime
5. Statement Lencane
6. I-akhawunti Yami
7. Khipha Imali
8. Ticelo & Sekelo
Simulator request format
The telco simulator and Postman collection post JSON to /api/v1/ussd/sessions.
{
"sessionId": "cust-balance-001",
"msisdn": "+26870000001",
"text": "",
"serviceCode": "*123#",
"networkCode": "ALL",
"newSession": true
}
The response body is plain text. Use the same sessionId while the USSD session is still active.
If your gateway environment enforces request signing, include the X-Emali2-Ussd-Key header in the simulator request. Public test and local flows may not require it.
Flow examples
1. Start a session and choose siSwati
BASE_URL="https://api.test.emali2.damplabs.com/api/v1/ussd/sessions"
SESSION_ID="cust-ss-001"
curl -sS "$BASE_URL" \
-H 'Content-Type: application/json' \
-d '{
"sessionId":"'"$SESSION_ID"'",
"msisdn":"+26870000001",
"text":"",
"serviceCode":"*123#",
"networkCode":"ALL",
"newSession":true
}'
curl -sS "$BASE_URL" \
-H 'Content-Type: application/json' \
-d '{
"sessionId":"'"$SESSION_ID"'",
"msisdn":"+26870000001",
"text":"2",
"serviceCode":"*123#",
"networkCode":"ALL",
"newSession":false
}'
2. Check balance
The balance flow is language-aware and PIN-gated.
BASE_URL="https://api.test.emali2.damplabs.com/api/v1/ussd/sessions"
SESSION_ID="cust-balance-001"
# Start session
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"",
"serviceCode":"*123#","networkCode":"ALL","newSession":true
}'
# Select English
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"1",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
# Open balance
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"1",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
# Enter customer PIN
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"123456",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
3. Send money
Dial *123# -> choose language -> 2. Send Money
-> enter recipient MSISDN -> enter amount -> enter PIN -> confirm
Use this flow for person-to-person transfers. The recipient MSISDN must be valid for the configured customer network rules.
4. Pay bills
The pay-bills menu is intentionally short:
- If the customer has recent or frequent billers, the top billers are shown first.
- If there are no recent billers, the menu falls back to manual entry.
- Manual entry is always the safe path for large biller catalogs.
Example manual flow:
Dial *123# -> choose language -> 3. Pay Bills
-> choose "Enter paybill manually"
-> enter paybill number
-> enter account number
-> enter amount
-> enter PIN
-> confirm
Example simulator sequence:
BASE_URL="https://api.test.emali2.damplabs.com/api/v1/ussd/sessions"
SESSION_ID="cust-bill-001"
MANUAL_OPTION="<option shown for Enter paybill manually>"
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"",
"serviceCode":"*123#","networkCode":"ALL","newSession":true
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"1",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"3",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"'"$MANUAL_OPTION"'",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"100100",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"04012345678",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"20",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"123456",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"1",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
For a manual SWSC payment, replace 100100 with 200200 and use the account reference WTR-2024-00156.
5. Buy airtime
Dial *123# -> choose language -> 4. Buy Airtime
-> choose network -> choose self or other
-> choose amount -> enter PIN -> confirm
6. Mini statement
Mini statement returns the latest 5 customer transactions, for example:
Mini Statement
Balance: E12326.10
1. Debit -E5 11/03 20:48
2. Debit -E20 11/03 20:48
3. Debit -E20 11/03 20:48
4. Debit -E13 11/03 20:46
5. Debit -E5 11/03 20:46
This flow is PIN-gated because it exposes account activity.
7. Cash out
Customer cash out uses manual till entry so the menu does not need to return every registered agent.
Dial *123# -> choose language -> 7. Cash Out
-> enter agent till
-> enter amount
-> enter PIN
-> confirm
-> receive withdrawal code
Example simulator sequence:
BASE_URL="https://api.test.emali2.damplabs.com/api/v1/ussd/sessions"
SESSION_ID="cust-cashout-001"
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"",
"serviceCode":"*123#","networkCode":"ALL","newSession":true
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"1",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"7",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"000001",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"50",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"123456",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
curl -sS "$BASE_URL" -H 'Content-Type: application/json' -d '{
"sessionId":"'"$SESSION_ID"'","msisdn":"+26870000001","text":"1",
"serviceCode":"*123#","networkCode":"ALL","newSession":false
}'
8. Requests and support
Use Requests & Support when a customer needs to approve a pending collection or another backend-driven request. The approval list is PIN-gated and the customer can approve or reject directly from USSD.
Security model
Important: The telco session identifies the customer by MSISDN, but sensitive data and money movement still require a valid customer PIN.
- Language selection can happen before the customer enters a PIN.
- Balance, mini statement, and request approvals require a PIN gate.
- Send money, pay bills, airtime, and cash out ask for the transaction PIN before final confirmation.
- Language can be changed later in
My Account -> Change Language.
Error handling and troubleshooting
| Symptom | What it usually means | What the customer should do |
|---|---|---|
Invalid option |
The menu choice does not exist in the current screen | Re-enter the option shown on screen |
Incorrect PIN |
Wrong customer PIN | Retry carefully; if repeated, reset through support or My Account flow |
Agent not found with identifier |
Invalid till number during cash out | Reconfirm the till number from the agent |
Amount must be between E50 and E3000 |
Cash-out amount is outside allowed limits | Enter a value inside the allowed range |
| Session restarted | Session timed out after inactivity | Dial again and restart the flow |
Testing assets
- Postman collection:
Emali2_USSD_Gateway_Collection.json - Local environment:
Emali2_USSD_Local.postman_environment.json - Remote environment:
Emali2_USSD_Remote.postman_environment.json
For agent-side operations, continue with the Agent USSD Guide.