# Response Format & Examples (/docs/products/ussd-flows/developers/exchange-api/response)

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](/docs/products/ussd-flows/developers/security/implement) has the code.

## Navigate

Sends the subscriber to another page, with optional data for it:

```json
{
  "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:

```json
{
  "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
- `action` is missing
- A `stop` reply has no `message`
- A `navigate` reply has no `pageId`, 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:

```json
{
  "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:

```json
{
  "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:

```json
{
  "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`:

```json
{
  "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:

```jsonc
// 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.

```json
{
  "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.
