This document provides a comprehensive analysis of the end-to-end request flow from incoming USSD requests through to the execution of DSL-defined applications on the Tech231 Platform.
Overview
The USSD execution engine follows a stateful session model built on Microsoft Orleans, where each user session is managed by a dedicated grain that maintains navigation history, session data, and execution state.
The platform provides two primary entry points for USSD traffic:
Telco Gateway (Tech231.Platform.Ussd.App) - Direct integration with mobile network operators
Management API (Tech231.Platform.Ussd.Api) - Provider-agnostic REST API for session management
The platform supports two ingress paths with different authentication and binding models:
1a. Telco Gateway Entry Point (Ussd.App)
The Telco Gateway (/gateway/{country}/{network}) is designed for direct integration with mobile network operators. It accepts requests via query parameters, body, or headers for maximum telco compatibility:
Code
GET /gateway/lr/orange?msisdn=231881234567&session=abc123&shortcode=*123%23&input=1
Request Binding Model:
Code
// Flexible binding from multiple sources for telco compatibilitypublic class GatewayRequestInput : RequestInput{ [FromRoute] public string Country { get; set; } [FromRoute] public string TelcoNetwork { get; set; }}public class RequestInput{ [FromQuery(Name = "msisdn"), FromBody, FromHeader(Name = "x-msisdn")] public string? Msisdn { get; set; } [FromQuery(Name = "session"), FromBody, FromHeader(Name = "x-session-id")] public string? DialogId { get; set; } // Session ID [FromQuery(Name = "shortcode"), FromBody] public string? ShortCode { get; set; } [FromQuery(Name = "input"), FromBody] public string? Input { get; set; }}
Key Features:
Rate Limiting: Redis-backed request throttling per MSISDN
Gateway Adapter Resolution: Looks up telco-specific adapter for response formatting
Tenant Resolution: Extracts tenant ID from gateway adapter metadata
1b. Management API Entry Point (Ussd.Api)
1b. Management API Entry Point (Ussd.Api)
Inbound USSD requests arrive at the normalized /api/v1/ussd/inbound endpoint:
public class SessionState{ public bool IsInitialized { get; set; } public SessionStatus Status { get; set; } public string? ApplicationName { get; set; } public string? Msisdn { get; set; } public string? ShortCode { get; set; } public string? NetworkId { get; set; } public string? CurrentMenu { get; set; } public List<string> NavigationHistory { get; set; } = []; public Dictionary<string, object> SessionData { get; set; } = []; public GlobalActionsConfig GlobalActions { get; set; } public DateTimeOffset CreatedAt { get; set; } public DateTimeOffset LastActivityAt { get; set; }}
Navigation Features
Back Navigation (FR-005)
Code
private async Task<UssdResponse> HandleBackNavigation(){ if (_state.State.NavigationHistory.Count <= 1) { // Already at start, stay at current menu return await NavigateToMenu(currentMenu); } // Pop current menu, navigate to previous _state.State.NavigationHistory.RemoveAt(_state.State.NavigationHistory.Count - 1); var previousMenuName = _state.State.NavigationHistory.Last(); return await NavigateToMenu(GetMenu(previousMenuName));}
Home Navigation (FR-006)
Code
private async Task<UssdResponse> HandleHomeNavigation(){ // Clear history and return to start menu _state.State.NavigationHistory.Clear(); var startMenu = GetMenu(_application.Start); return await NavigateToMenu(startMenu);}
Terminal Menus (FR-003, FR-007)
Code
private async Task<UssdResponse> NavigateToMenu(MenuBlock menu){ // Menu is terminal if: no options OR explicit terminal=true flag if (menu.IsTerminal) { _state.State.Status = SessionStatus.Completed; return UssdResponse.End(renderedText, _sessionId, menu.Name); } return UssdResponse.Continue(renderedText, _sessionId, menu.Name);}
Observability
Metrics (OpenTelemetry)
ussd.session.started - Counter for new sessions
ussd.session.completed - Counter for completed sessions
ussd.session.error - Counter for session errors
ussd.input.processed - Counter for input processing
Tracing
Each request includes a trace ID in the response for correlation: