Products · Exchange & Dynamic API
Response Format & Examples
The navigate and stop replies your server returns, what happens when a call fails, and worked examples.
Your server replies with one of two actions: navigate sends the subscriber to another page, and stop ends the session with a message. Encrypt the reply JSON with the AES key from the request and return the base64 text as the body, with HTTP status 200. Decrypting & Responding has the code.
Navigate
Sends the subscriber to another page, with optional data for it:
{
"action": "navigate",
"pageId": "show_balance",
"pageData": {
"balance": "10,000.00",
"currency": "SLE",
"account_type": "Savings"
}
}| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | "navigate" |
pageId | string | Yes | ID of a page in the flow |
pageData | object | No | Data for that page, read as $(data.KEY). A value can be an array, for menu items. |
If pageId is a display page, the subscriber sees it and the session ends.
Stop
Ends the session and shows a message:
{
"action": "stop",
"message": "You sent SLE 500 to ACC12345678. Reference: TXN-9874."
}| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | "stop" |
message | string | Yes | Text shown to the subscriber before the session ends |
When a call fails
The session ends and the subscriber sees an error if:
- Your server doesn't reply within 10 seconds
- The HTTP status isn't 200
- Monime can't decrypt the reply, or the result isn't valid JSON
actionis missing- A
stopreply has nomessage - A
navigatereply has nopageId, or names a page that isn't in the flow
Monime doesn't retry a failed call.
Examples
Balance check
The subscriber picks an account, your server looks up the balance, and the next page shows it:
{
"version": "1.0",
"pages": [
{
"id": "select_account",
"component": {
"type": "menu",
"label": "Select account type:",
"items": [
{"value": "savings", "label": "Savings"},
{"value": "current", "label": "Current"}
]
},
"on_done": {
"action": "exchange",
"url": "https://api.kabalafarms.sl/ussd/get-balance"
}
},
{
"id": "show_balance",
"component": {
"type": "display",
"label": "$(data.account_type) account\nBalance: $(data.currency) $(data.balance)"
}
}
]
}Your server gets flowData.select_account = "savings", looks up the balance and replies:
{
"action": "navigate",
"pageId": "show_balance",
"pageData": {
"account_type": "Savings",
"currency": "SLE",
"balance": "10,000.00"
}
}Menu built from live data
A dynamic page asks your server for the subscriber's accounts, and a menu page lists them:
{
"version": "1.0",
"pages": [
{
"id": "pick_account",
"component": {
"type": "dynamic",
"url": "https://api.kabalafarms.sl/ussd/accounts"
}
},
{
"id": "select_account",
"component": {
"type": "menu",
"label": "Choose an account:",
"items": "$(data.accounts)"
}
}
]
}Your server replies with the menu items in pageData:
{
"action": "navigate",
"pageId": "select_account",
"pageData": {
"accounts": [
{"value": "ACC001", "label": "Savings - SLE 10,000"},
{"value": "ACC002", "label": "Current - SLE 2,500"}
]
}
}Different replies for different input
Your server can read flowData and reply in different ways:
// The subscriber confirmed the transfer:
{ "action": "navigate", "pageId": "processing" }
// The subscriber cancelled:
{ "action": "stop", "message": "Transfer cancelled." }
// The amount is out of range:
{ "action": "navigate", "pageId": "enter_amount_retry",
"pageData": { "error": "Amount must be between SLE 10 and SLE 50,000. Enter a new amount." } }Good practice
Reply with stop when something goes wrong
If your server hits an error it can handle, reply with a stop that tells the subscriber what happened and what to do. Keep internal details out of it, and log them on your server instead.
{
"action": "stop",
"message": "We couldn't finish your transfer. Please try again in a few minutes."
}Encrypt this reply too. A plain JSON reply fails like any other bad reply.
Reply quickly
The 10-second limit includes network time. Aim for well under 5 seconds, and cache lookups where you can.
Make irreversible steps idempotent
sessionId is the same for every call in a session, and a flow can reach the same page twice. Before you move money or place an order, check whether you already did it, for example by keying on sessionId plus currentPage or on your own reference.
Check input before acting on it
flowData holds whatever the subscriber typed. Check it against your rules before a transfer or purchase. If it's wrong, send the subscriber to a page where they can try again.