Inbound messages
Every text your customers send to your shortcodes is stored — pull them over the API with filters on shortcode, keyword, routing outcome and time window.
This is the pull counterpart to the push-style inbound webhooks. Use the webhook for real-time handling and this API for backfills, audits, dashboards, or when you'd rather poll than host an endpoint.
List inbound messages
GET/api/v1/inbound
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
| shortcode | string | No | Only messages to this shortcode. |
| keyword | string | No | Only messages that matched this keyword. |
| status | string | No | Filter by routing_status — see the table below. |
| from / to | RFC 3339 timestamp | No | Received-at window, e.g. from=2026-07-01T00:00:00Z. |
| page / page_size | int | No | Standard pagination. |
curl "https://api.mobilesasa.com/api/v1/inbound?shortcode=40404&keyword=BALANCE&from=2026-07-01T00:00:00Z&page=1" \
-H "Authorization: Bearer $MOBILESASA_TOKEN"Response — 200
{
"success": true,
"data": [
{
"uuid": "b8d3…",
"msisdn": "254712345678",
"message": "BALANCE 4471",
"shortcode": "40404",
"keyword": "BALANCE",
"provider": "safaricom",
"routing_status": "callback",
"received_at": "2026-07-11T13:22:08Z",
"created_at": "2026-07-11T13:22:09Z"
}
],
"meta": { "page": 1, "page_size": 20, "total": 1284 }
}What routing_status tells you
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| direct | dedicated | No | Arrived on your own shortcode — no keyword needed, it's all yours. |
| matched | shared | No | Matched one of your keywords on the shared shortcode. |
| callback | forwarded | No | Delivered to the keyword's callback URL successfully. |
| survey | routed | No | Opened or continued a survey session — the reply lives in the survey's responses. |
| unmatched | dropped | No | Shared-shortcode message that matched none of your keywords — you only see these on your own shortcodes. |
Reconciling webhook deliveries
Webhooks are at-least-once and your endpoint can be down. A periodic sweep of this API filtered to
status=matched (received but not confirmed forwarded) is a cheap way to catch anything your webhook missed.