USSD Webhooks

The endpoints carriers call to deliver live USSD sessions to your service.

Carriers call these endpoints to hand off each session event. Register the URLs in your carrier portal (or ask an admin to). Unlike the rest of the API, these routes take no authentication token, they are publicly accessible endpoints trusted by carrier IP ranges, so never put secrets in the URLs.

No Bearer token here

Webhook routes are called by the carrier, not by your server. They are unauthenticated by design. Do not send an Authorization header, and do not expose these URLs as if they were your API token endpoints.

Safaricom webhook

GET/webhooks/ussd/safaricomalso accepts POST

Safaricom sends session events as query parameters. Your response must be plain text (not JSON) starting with CON to continue the session or END to terminate it.

Received query params
MSISDNreq
string

Subscriber phone number

SERVICE_CODEreq
string

USSD code that was dialed

SESSION_IDreq
string

Unique session identifier from the carrier

USSD_STRING
string

All input so far in the session (e.g. *657*1*2#)

Response format (plain text, not JSON)
CON Acme Services
1. Check balance
2. Buy data
0. Exit

# End the session
END Thank you for using Acme Services.

Generic JSON webhook

POST/webhooks/ussd/inbound

For other carriers that speak a JSON API. Both the request from the carrier and your response use JSON.

Request body from carrier
msisdnreq
string

Subscriber phone

service_codereq
string

USSD code dialed

session_idreq
string

Carrier session ID

ussd_string
string

Subscriber input so far

network
string

Network identifier (airtel, telkom, equitel)

Request from carrier
{
  "msisdn": "+254712345678",
  "service_code": "*657#",
  "session_id": "carrier-sess-abc123",
  "ussd_string": "*657*1#",
  "network": "airtel"
}
Your response
{
  "response": "CON Acme Services\n1. Check balance\n2. Buy data",
  "end_session": false
}

Callback mode, your own server

When a code or extension is set to mode=callback, the platform forwards each session event to your callback_url as a POST. Your server must respond within 3 seconds with a JSON body.

POST to your callback_url for each session event
{
  "session_id":   "carrier-sess-abc123",
  "service_code": "*657#",
  "msisdn":       "+254712345678",
  "ussd_string":  "*657*1#",
  "network":      "safaricom"
}
Your response (continue, or end)
// continue the session
{ "response": "CON Your balance is KES 1,200.\n0. Back", "end_session": false }

// or end it
{ "response": "END Thank you!", "end_session": true }

Test the whole flow first

Exercise your menus and callback handlers end to end with USSD simulation before pointing a carrier at these webhook URLs.