Server

USSD Session Flow, Explained

By Prabath Thalangama· October 24, 2026· 3 min read
#ussd#ss7#telecom

Introduction

USSD (Unstructured Supplementary Service Data) is the interactive text menu behind codes like *123# — balance checks, mobile money, roaming menus. Unlike SMS it's session-oriented and real-time: the dialogue stays open while the user navigates, and nothing is stored.

Two modes

  • Pull / MMO (Mobile-originated) — the user dials *123#. The network opens a session to the application, which returns a menu; the user replies, the app responds, until one side ends it.
  • Push / MTO (Mobile-terminated) — the network/app initiates a prompt to the handset ("Enter PIN to confirm payment"), the user responds.

Each response from the app is either:

  • USSD-Request — "here's a menu, I expect a reply" → session stays open.
  • USSD-Notify — "here's a message, no reply needed" → session ends after the user dismisses it.

The message flow (pull, GSM/UMTS)

Handset          MSC/VLR           HLR / gsmSCF         USSD Gateway / App
  |  *123#  (RIL) |                     |                      |
  |--------------->|  MAP PROCESS_UNSTRUCTURED_SS_REQUEST      |
  |               |------------------------------------------->|  (routed by the
  |               |                     |                      |   service code)
  |               |                     |    app builds menu   |
  |               |<-------------------------------------------|  MAP USSD-Request
  |  menu shown   |   (RIL)             |    "1 Balance\n2 Buy" |
  |<--------------|                     |                      |
  |  user: "1"    |                     |                      |
  |-------------->|  MAP UNSTRUCTURED_SS_REQUEST (the reply)    |
  |               |------------------------------------------->|
  |               |<-------------------------------------------|  USSD-Request or
  |  result shown |                     |   "Balance: $4.20"   |  USSD-Notify (end)
  |<--------------|                     |                      |
  • The service code (*123#, *100#) determines routing. Codes are allocated by the operator; some are standardised (*#06# = IMEI, handled locally by the phone, never hits the network).
  • On the SS7 side it's MAP operations over TCAP: processUnstructuredSS-Request (first), unstructuredSS-Request (app→user prompt), unstructuredSS-Notify (app→user, no reply). Modern gateways speak these over SIGTRAN and expose an HTTP/XML or SMPP-like API to the application.
  • dcs (data coding scheme) — usually GSM 7-bit; 0x0F is common.

Session characteristics

  • Stateful, short-lived. The MSC/VLR holds the session. There's a network timeout (often ~30–180s of inactivity) after which the session is torn down and the user sees "session expired".
  • No delivery receipts, no store-and-forward. If the user has no signal, it just fails.
  • One session per subscriber at a time on most networks.
  • Payload limit ~182 bytes (GSM 7-bit ~160 chars) per message — design menus to fit.

Integrating an application

Aggregators/operators expose USSD to apps as an HTTP callback:

POST /ussd  (from the gateway)
  sessionId=abc123
  msisdn=94771234567
  serviceCode=*123#
  text=            <- empty on first request; "1" then "1*2" as the user drills in

Response body:
  CON Welcome
  1. Balance
  2. Buy data          <- "CON" = expect more input (session continues)

  ...later...
  END Your balance is $4.20     <- "END" = terminate the session

text accumulates the user's path (1*2*100), so the app is effectively stateless per request — parse text to know where the user is. Keep a server-side session keyed by sessionId only if you need more than the path.

Verification and troubleshooting

# On the SS7/SIGTRAN side
tshark -i any -O tcap,gsm_ss -d sctp.port==2905,m3ua
  • *123# returns "connection problem or invalid MMI code" — no route for that service code, the gateway/app is down, or the SCCP GT for the USSD gateway isn't reachable. Also thrown for a genuinely malformed code.
  • Session expires mid-menu — user took too long (network inactivity timer), or the app's response exceeded the size limit and was rejected, or the app replied with CON/USSD-Request but the transport dropped.
  • Menu shows but selections do nothing — the app isn't receiving the follow-up (routing only set up for the first processUnstructuredSS), or it's not parsing the accumulated text.
  • Works on-net, fails for roamers — inbound roamers' USSD may be handled by the home network (USSD is anchored at the VLR); "USSD when roaming" needs the home gsmSCF reachable via SS7 from the visited MSC.
  • Non-Latin characters broken — dcs / encoding mismatch; USSD UCS-2 support is spotty on older handsets.
  • App gets duplicate callbacks — the gateway retried on a slow response. Respond within ~1–2s; make handlers idempotent on sessionId+text.
PrabathStuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.