Overview
The IXO USSD gateway exposes a single session endpoint that telecom gateways call for each user interaction, plus debug and health endpoints for operators.
All endpoints accept and return JSON unless otherwise noted. The main session endpoint returns plain text in the USSD response format.
Authentication
The /api/ussd endpoint does not require authentication by default. It is intended to be called by your telecom gateway server, not directly from a browser. Restrict access at the network or reverse-proxy layer in production.
Base URL
http://YOUR_SERVER:3000The default port is 3000. Set PORT in your environment to override.
Session endpoint
POST /api/ussd
Processes a USSD interaction. Called by the telecom gateway once per user input during a session.
Request
{
"sessionId": "string",
"serviceCode": "string",
"phoneNumber": "string",
"text": "string"
}`sessionId`
- Type:
string - Required: yes
- Description: Unique session identifier assigned by the telecom gateway.
`serviceCode`
- Type:
string - Required: yes
- Description: USSD service code the user dialled, for example
*1234#.
`phoneNumber`
- Type:
string - Required: yes
- Description: User phone number in E.164 international format, for example
+260971234567.
`text`
- Type:
string - Required: yes
- Description: User input accumulated during the session; empty string on initial dial.
Validation rules
phoneNumbermust be E.164 formatserviceCodemust match USSD format, e.g.*1234#or*123*456#textmaximum 182 characters (USSD protocol limit)sessionIdmaximum 100 characters
Response
Plain text. The first word indicates whether the session continues or ends.
CON <menu text>END <closing message>`CON`
- Meaning: Session continues; display the menu and wait for the next input.
`END`
- Meaning: Session closed; display the message and end the USSD session.
Example — initial dial
curl -X POST http://localhost:3000/api/ussd \
-H "Content-Type: application/json" \
-d '{
"sessionId": "session-001",
"serviceCode": "*1234#",
"phoneNumber": "+260971234567",
"text": ""
}'Response:
CON Welcome to IXO USSD
1. Know More
2. Account Menu
*. ExitExample — user selects option 1
curl -X POST http://localhost:3000/api/ussd \
-H "Content-Type: application/json" \
-d '{
"sessionId": "session-001",
"serviceCode": "*1234#",
"phoneNumber": "+260971234567",
"text": "1"
}'Response:
CON Information Center
1. About this service
2. Pricing
3. Delivery
*. ExitDebug endpoints
GET /api/ussd/debug/:sessionId
Returns the current state and context for an active session. Intended for operator debugging only — do not expose publicly.
Response
{
"sessionId": "session-001",
"debugInfo": {
"state": "preMenu",
"context": {
"sessionId": "session-001",
"phoneNumber": "+260971234567",
"message": "Welcome to IXO USSD...",
"isAuthenticated": false
},
"status": "active"
},
"activeSessions": ["session-001"]
}GET /api/ussd/sessions
Returns all currently active session IDs.
{
"activeSessions": ["session-001", "session-002"],
"count": 2
}Health endpoints
GET /health
Returns application health with database connectivity status.
{
"status": "ok",
"environment": "production",
"timestamp": "2026-04-22T10:00:00.000Z",
"uptime": 3600,
"database": "connected"
}GET /
Basic health check for platform-as-a-service health probes.
{
"status": "ok",
"message": "ixo-ussd-server running",
"environment": "production",
"timestamp": "2026-04-22T10:00:00.000Z"
}Errors
`END Invalid input. Please try again.`
- Cause: Request failed validation.
- What to do: Check phone number is E.164, service code format, and text length.
Session not found on `GET /debug/:sessionId`
- Cause: Session expired or never started.
- What to do: Sessions expire after inactivity; the user must dial again.
Rate limiting: 100 requests per minute per IP address. Requests exceeding this return HTTP 429.
For generic HTTP status handling (retries, 5xx, client errors), see Error handling. For environment-specific service URLs and auth, see Networks and endpoints and Authentication matrix.
Security notes
- User PINs are encrypted at rest with AES-256 using the
PIN_ENCRYPTION_KEYenvironment variable. - All inputs are validated and sanitised before processing.
- Set
TRUST_PROXY_ENABLED=truewhen running behind a reverse proxy so rate limiting uses the real client IP.