This document provides concrete example USSD request payloads that can be used to test USSD applications on the Tech231 Platform.
Gateway Endpoint Examples (Telco Traffic)
The gateway endpoint (/gateway/{country}/{network}) is designed for direct integration with mobile network operators. It accepts requests via GET with query parameters or POST with form/JSON body.
1. Session Initiation (New Session)
GET Request (Query Parameters):
GET /gateway/liberia/mtn?msisdn=231881234567&session=sess-12345&shortcode=*123%23&input= HTTP/1.1
Host: ussd.platform.tech231.com
Accept: text/plain
POST Request (JSON Body):
POST /gateway/liberia/mtn HTTP/1.1
Host: ussd.platform.tech231.com
Content-Type: application/json
Accept: text/plain
{
"msisdn": "231881234567",
"dialogId": "sess-12345",
"shortCode": "*123#",
"input": ""
}
Expected Response (Session Continues):
CON Welcome to Banking App
1. Check Balance
2. Transfer Money
3. Buy Airtime
0. Back
2. Session Continuation (User Input)
GET Request:
GET /gateway/liberia/mtn?msisdn=231881234567&session=sess-12345&shortcode=*123%23&input=1 HTTP/1.1
Host: ussd.platform.tech231.com
Accept: text/plain
POST Request (JSON Body):
POST /gateway/liberia/mtn HTTP/1.1
Host: ussd.platform.tech231.com
Content-Type: application/json
{
"msisdn": "231881234567",
"dialogId": "sess-12345",
"shortCode": "*123#",
"input": "1"
}
Expected Response:
CON Your current balance is $150.00
Press any key to continue
0. Back
When the session reaches a terminal menu (no further input expected):
END Thank you for using Banking App!
Transaction ID: TXN-2024-001234
Your balance: $100.00
Management API Examples (Internal/Partner Traffic)
The Management API (/api/v1/ussd/*) is designed for programmatic session control from internal services or partner integrations.
1. Start Session via Session Endpoint
Request:
POST /api/v1/ussd/session HTTP/1.1
Host: api.platform.tech231.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"applicationName": "banking-app",
"msisdn": "231881234567",
"shortCode": "*123#",
"networkId": "orange"
}
Response (201 Created):
{
"sessionId" : "api-sess-67890" ,
"text" : "Welcome to Banking App \n 1. Check Balance \n 2. Transfer Money \n 3. Buy Airtime \n 0. Back" ,
"responseType" : "Continue" ,
"currentMenu" : "MainMenu"
}
2. Process Input via Session Endpoint
Request:
POST /api/v1/ussd/session/api-sess-67890/input HTTP/1.1
Host: api.platform.tech231.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"input": "1"
}
Response (200 OK):
{
"sessionId" : "api-sess-67890" ,
"text" : "Your current balance is $150.00 \n Press any key to continue \n 0. Back" ,
"responseType" : "Continue" ,
"currentMenu" : "BalanceDisplay"
}
3. Normalized Inbound Request
The inbound endpoint provides provider-agnostic session handling:
Session Initiation:
POST /api/v1/ussd/inbound HTTP/1.1
Host: api.platform.tech231.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"sessionId": "inbound-sess-11111",
"msisdn": "231881234567",
"shortCode": "*123#",
"provider": "Orange",
"input": "",
"isInitiation": true
}
Response:
{
"sessionId" : "inbound-sess-11111" ,
"text" : "Welcome to Banking App \n 1. Check Balance \n 2. Transfer Money \n 3. Buy Airtime \n 0. Back" ,
"responseType" : "Continue" ,
"traceId" : "0af7651916cd43dd8448eb211c80319c"
}
Session Continuation:
POST /api/v1/ussd/inbound HTTP/1.1
Host: api.platform.tech231.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"sessionId": "inbound-sess-11111",
"msisdn": "231881234567",
"shortCode": "*123#",
"provider": "Orange",
"input": "2",
"isInitiation": false
}
4. Get Session State
Request:
GET /api/v1/ussd/session/api-sess-67890 HTTP/1.1
Host: api.platform.tech231.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Response:
{
"sessionId" : "api-sess-67890" ,
"status" : "Active" ,
"applicationName" : "banking-app" ,
"msisdn" : "231881234567" ,
"shortCode" : "*123#" ,
"currentMenu" : "TransferMenu" ,
"navigationHistory" : [ "MainMenu" , "TransferMenu" ],
"sessionData" : {
"accountNumber" : "****1234" ,
"lastBalance" : "150.00"
},
"startedAt" : "2024-01-15T10:30:00Z" ,
"lastActivityAt" : "2024-01-15T10:31:45Z"
}
5. Terminate Session
Request:
DELETE /api/v1/ussd/session/api-sess-67890 HTTP/1.1
Host: api.platform.tech231.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Response (204 No Content)
Here's a complete DSL definition and test scenarios for a simple banking application:
DSL Definition
application "banking-app" {
start = "MainMenu"
options {
global_actions {
back_key = "0"
home_key = "00"
}
}
menu "MainMenu" {
text = "Welcome to Banking App"
option "1" {
label = "Check Balance"
goto = "BalanceDisplay"
}
option "2" {
label = "Transfer Money"
goto = "TransferMenu"
}
option "3" {
label = "Buy Airtime"
goto = "AirtimeMenu"
}
}
menu "BalanceDisplay" {
text = "Your current balance is ${ {session.data.balance} } \n Press any key to continue"
terminal = true
}
menu "TransferMenu" {
text = "Enter recipient phone number:"
option "*" {
goto = "TransferAmount"
actions = [{ type = "set" , key = "recipient" , value = "{{session.input}}" }]
}
}
menu "TransferAmount" {
text = "Enter amount to transfer to {{session.data.recipient}}:"
option "*" {
goto = "TransferConfirm"
actions = [{ type = "set" , key = "amount" , value = "{{session.input}}" }]
}
}
menu "TransferConfirm" {
text = "Confirm transfer of ${ {session.data.amount} } to {{session.data.recipient}}?"
option "1" {
label = "Confirm"
goto = "ExecuteTransfer"
}
option "2" {
label = "Cancel"
goto = "MainMenu"
}
}
action "ExecuteTransfer" {
type = "http"
method = "POST"
url = "{{env.BANKING_API_URL}}/transfers"
body = {
from = "{{session.msisdn}}"
to = "{{session.data.recipient}}"
amount = "{{session.data.amount}}"
}
next = "TransferSuccess"
error = "TransferFailed"
}
menu "TransferSuccess" {
text = "Transfer successful! \n Transaction ID: {{response.transactionId}} \n New balance: ${ {response.newBalance} } "
terminal = true
}
menu "TransferFailed" {
text = "Transfer failed: {{response.error}} \n Please try again later."
terminal = true
}
menu "AirtimeMenu" {
text = "Select airtime amount:"
option "1" {
label = "$5"
goto = "BuyAirtime"
actions = [{ type = "set" , key = "airtimeAmount" , value = "5" }]
}
option "2" {
label = "$10"
goto = "BuyAirtime"
actions = [{ type = "set" , key = "airtimeAmount" , value = "10" }]
}
option "3" {
label = "$20"
goto = "BuyAirtime"
actions = [{ type = "set" , key = "airtimeAmount" , value = "20" }]
}
}
action "BuyAirtime" {
type = "http"
method = "POST"
url = "{{env.AIRTIME_API_URL}}/purchase"
body = {
msisdn = "{{session.msisdn}}"
amount = "{{session.data.airtimeAmount}}"
}
next = "AirtimeSuccess"
error = "AirtimeFailed"
}
menu "AirtimeSuccess" {
text = "Airtime purchased! \n Amount: ${ {session.data.airtimeAmount} } \n Reference: {{response.reference}}"
terminal = true
}
menu "AirtimeFailed" {
text = "Airtime purchase failed. Please try again."
terminal = true
}
}
Test Scenarios
Scenario 1: Check Balance Flow
Step Input Expected Menu Response Type 1 (dial *123#) MainMenu Continue 2 "1" BalanceDisplay End
Scenario 2: Transfer Money Flow
Step Input Expected Menu Response Type 1 (dial *123#) MainMenu Continue 2 "2" TransferMenu Continue 3 "231887654321" TransferAmount Continue 4 "50" TransferConfirm Continue 5 "1" TransferSuccess or TransferFailed End
Scenario 3: Back Navigation
Step Input Expected Menu Response Type 1 (dial *123#) MainMenu Continue 2 "2" TransferMenu Continue 3 "0" (Back) MainMenu Continue
Scenario 4: Home Navigation
Step Input Expected Menu Response Type 1 (dial *123#) MainMenu Continue 2 "2" TransferMenu Continue 3 "231887654321" TransferAmount Continue 4 "00" (Home) MainMenu Continue
cURL Examples for Quick Testing
Gateway - New Session
curl -X GET "http://localhost:5000/gateway/liberia/mtn?msisdn=231881234567&session=test-$( date +%s)&shortcode=*123%23&input="
Gateway - Send Input
curl -X GET "http://localhost:5000/gateway/liberia/mtn?msisdn=231881234567&session=test-1234&shortcode=*123%23&input=1"
API - Start Session (with JWT)
curl -X POST "http://localhost:5000/api/v1/ussd/session" \
-H "Authorization: Bearer $JWT_TOKEN " \
-H "Content-Type: application/json" \
-d '{
"applicationName": "banking-app",
"msisdn": "231881234567",
"shortCode": "*123#"
}'
API - Process Input
curl -X POST "http://localhost:5000/api/v1/ussd/session/ $SESSION_ID /input" \
-H "Authorization: Bearer $JWT_TOKEN " \
-H "Content-Type: application/json" \
-d '{"input": "1"}'
Error Response Examples
Invalid MSISDN
{
"sessionId" : "test-session" ,
"errorCode" : "INVALID_MSISDN" ,
"message" : "MSISDN must be 10-15 digits" ,
"traceId" : "abc123..."
}
Application Not Found
{
"sessionId" : "test-session" ,
"errorCode" : "APP_NOT_FOUND" ,
"message" : "No application found for this shortcode" ,
"traceId" : "abc123..."
}
Session Not Found
{
"sessionId" : "expired-session" ,
"errorCode" : "SESSION_NOT_FOUND" ,
"message" : "Session not found or expired" ,
"traceId" : "abc123..."
}
See Also
Last modified on January 26, 2026