USSD codes & extensions

Two ways onto the dial pad: apply for your own dedicated code, or take an extension on a shared code (*657*45#) and be live in days instead of weeks.

Endpoints live under /api/v1/ with a scoped mbs_ token or JWT. Once a code or extension is approved, you decide how sessions are answered. A hosted menu, a survey, or callback mode against your own backend.

Fees

Approval checks your wallet, and fails if it can't pay

USSD approvals deduct setup and monthly fees from your KES wallet, and unlike sender IDs the deduction is checked at approval time. An approval with an underfunded wallet fails outright. Fund the wallet before your application reaches the front of the queue. Extension pricing varies with digit length (shorter = pricier); dedicated codes are quoted per application.

Option A: extension on a shared code

GET/api/v1/ussd/shared-codes
GET/api/v1/ussd/shared-codes/{uuid}/availability

List the platform's shared codes, then check whether the extension you want is free:

Shared codes listing (the uuid is your ussd_code_id)
{
  "success": true,
  "data": [
    { "uuid": "6a77e0b2-…", "code": "657", "networks": ["safaricom", "airtel", "telkom"] }
  ]
}
curl "https://api.mobilesasa.com/api/v1/ussd/shared-codes/6a77e0b2-…/availability?extension=45" \
  -H "Authorization: Bearer $MOBILESASA_TOKEN"
POST/api/v1/ussd/extensions

Parameters

FieldTypeRequiredDescription
ussd_code_iduuidYesUUID of the shared code (from the listing above).
extensionstringYesThe digits after the star. Customers dial *657*45# for extension 45. 1–2 digit extensions cost more than longer ones.
networksstring[]YesNetworks the extension must answer on.
curl -X POST https://api.mobilesasa.com/api/v1/ussd/extensions \
  -H "Authorization: Bearer $MOBILESASA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ussd_code_id": "6a77e0b2-…",
    "extension": "45",
    "networks": [
      "safaricom",
      "airtel"
    ]
  }'

Option B: dedicated code

POST/api/v1/ussd/codes

A dedicated code (*728#) is registered with the networks in your name. Expect network paperwork and a longer lead time. Send JSON for a bare application, or multipart/form-data when attaching supporting documents (authorisation letters, business certificate. 10 MB max per file; the file field name becomes the document type, with an optional network_{field} form value scoping it to one network).

curl -X POST https://api.mobilesasa.com/api/v1/ussd/codes \
  -H "Authorization: Bearer $MOBILESASA_TOKEN" \
  -F "code=*728#" \
  -F "networks=safaricom" \
  -F "networks=airtel" \
  -F "[email protected]" \
  -F "network_request_letter=safaricom"

JSON mode works too when there are no documents: {"code": "*728#", "networks": ["safaricom","airtel"]}.

Track the application

GET/api/v1/ussd/codes
GET/api/v1/ussd/codes/{uuid}
GET/api/v1/ussd/extensions
GET/api/v1/ussd/extensions/{uuid}

Both codes and extensions carry an approval_status (pending → approved | rejected, with rejection_reason on rejects. You're also emailed automatically either way) and a runtime status once live.

curl "https://api.mobilesasa.com/api/v1/ussd/extensions/91c2f7aa-…" \
  -H "Authorization: Bearer $MOBILESASA_TOKEN"

Point it somewhere

PUT/api/v1/ussd/codes/{uuid}
PUT/api/v1/ussd/extensions/{uuid}

Parameters

FieldTypeRequiredDescription
modestringNomenu (hosted menu), survey, or callback (your backend drives every screen, see callback mode).
menu_iduuidNoThe approved menu to serve, when mode is menu.
survey_iduuidNoThe survey to run, when mode is survey.
callback_urlstringNoYour HTTPS endpoint, when mode is callback.
curl -X PUT https://api.mobilesasa.com/api/v1/ussd/extensions/91c2f7aa-… \
  -H "Authorization: Bearer $MOBILESASA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "callback",
    "callback_url": "https://example.com/ussd"
  }'