USSD menus & nodes

Build a dial-pad menu tree we host and serve for you — no server on your side, sub-second screens, with callback nodes where you do need your own logic.

A menu is the container (header text, invalid-input text, approval state); nodes are the tree — each node is one option a caller can pick, and its children are the next screen. Attach the approved menu to a code or extension with mode=menu.

Create the menu

POST/api/v1/ussd/menus

Parameters

FieldTypeRequiredDescription
namestringYesInternal label.
header_textstringYesThe first line of the root screen — "Welcome to Dukapay".
invalid_input_textstringYesShown when the caller picks an option that doesn't exist.
descriptionstringNoNotes for your team and the reviewers.
curl -X POST https://api.mobilesasa.com/api/v1/ussd/menus \
  -H "Authorization: Bearer $MOBILESASA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Dukapay main menu",
    "header_text": "Welcome to Dukapay",
    "invalid_input_text": "Invalid choice. Please try again."
  }'

Add nodes

POST/api/v1/ussd/menus/{uuid}/nodes

Parameters

FieldTypeRequiredDescription
option_numberstringYesThe digit(s) the caller presses to pick this node — "1", "2", "0".
labelstringYesThe text shown next to the option number in the parent screen.
typestringYesWhat happens when picked — see the node types below.
display_textstringNoThe screen this node shows: the header of its sub-menu, the final text of a message node, or the prompt of an input node.
parent_iduuidNoUUID of the parent node. Omit for a root-level option.
callback_urlstringNoFor callback nodes — where the session is handed to your backend.
sort_orderintNoDisplay order among siblings.

Node types

FieldTypeRequiredDescription
submenubranchNoShows display_text as a header plus its children as numbered options.
messageterminalNoShows display_text and ends the session — balances, confirmations, goodbyes.
inputcollectNoPrompts with display_text and captures whatever the caller types (an amount, an account number); its child nodes continue the flow.
callbackhand-offNoForwards the session to your callback_url — from here your backend drives the screens, same contract as callback mode.
backnavigationNoReturns the caller to the previous screen. Convention: option_number "0".
curl -X POST https://api.mobilesasa.com/api/v1/ussd/menus/4b8e19c3-…/nodes \
  -H "Authorization: Bearer $MOBILESASA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "option_number": "1",
    "label": "Check balance",
    "type": "callback",
    "callback_url": "https://example.com/ussd/balance",
    "sort_order": 1
  }'

Build depth by chaining parent_id: create "2. My account" as a submenu, then create its options with parent_id set to that node's UUID. Nodes can be updated (PUT /ussd/menus/nodes/{uuid}) and deleted (DELETE) while the menu is in draft.

Simulate before submitting

POST/api/v1/ussd/simulate

Walk the tree exactly as a phone would — ussd_string is the caller's input path so far ("1", then "1*3", …):

curl -X POST https://api.mobilesasa.com/api/v1/ussd/simulate \
  -H "Authorization: Bearer $MOBILESASA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "service_code": "*384*45#",
    "session_id": "sim-001",
    "ussd_string": "1",
    "network": "safaricom"
  }'
Response — 200
{
  "success": true,
  "data": {
    "prompt": "My account\n1. Balance\n2. Statements\n0. Back",
    "end_session": false
  }
}

Submit for approval

POST/api/v1/ussd/menus/{uuid}/submit
POST/api/v1/ussd/menus/{uuid}/revise

Menus go through a quick content review before serving live traffic (you're emailed on approval or rejection, with the reason). A pending or approved menu is locked— nodes can't change. To edit a live menu, call revise: it unlocks a draft revision you edit and re-submit, while the approved version keeps serving until the revision replaces it.

curl -X POST https://api.mobilesasa.com/api/v1/ussd/menus/4b8e19c3-…/submit \
  -H "Authorization: Bearer $MOBILESASA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

Test-only whitelist

GET/api/v1/ussd/menus/{uuid}/whitelist
PUT/api/v1/ussd/menus/{uuid}/whitelist

Restrict a menu to your test numbers while iterating: {"msisdns": ["254712345678"]} — an empty array lifts the restriction.

Menu, survey or callback?

Static information trees → hosted menu (this page). Structured Q&A with stored answers → survey. Anything dynamic per caller (balances, payments) → callback mode, or a menu whose leaves are callback nodes so only the dynamic screens hit your server.