Request & response conventions
This page documents the contract that every Merchants API endpoint follows. Endpoint pages only specify what is unique to that endpoint (path, body fields, payload shape, error codes); everything below applies globally.
Requests
- Header:
Content-Type: application/json. - Header:
API-KEY: <your-api-key>(see Authentication). - Body: JSON object
Response schema:
{
"status": "SUCCESS",
"message": "",
"status_code": 200,
"payload": { }
}
Field semantics:
| Field | Type | Meaning |
|---|---|---|
status | "SUCCESS" | "FAIL" | Coarse outcome. Branch on this field to detect business-logic failures. |
message | string | Short machine-readable code (e.g. "INVALID_MSISDN", "CARRIER_NOT_SUPPORTED"). |
status_code | integer | Mirrors the HTTP response status. Included for clients that only have access to the response body (e.g. behind a transport that strips HTTP). |
payload | object | null | Endpoint-specific shape on success. Omitted (or null) on failures. |
warning
status: "FAIL"Several carrier-related failures return HTTP 200 with status: "FAIL". Example:
CARRIER_NOT_SUPPORTED: The detected carrier is not provisioned for the requested service.INVALID_PIN,PIN_EXPIRED,MAX_PIN_RETRIES: OTP verification failed.- Carrier rejections such as
INSUFFICIENT_FUNDS,BLACKLISTED,BARRED.
The reasoning: the request reached the API and was structurally valid, but the underlying carrier (or the user) refused.
Clients must branch on status, not the HTTP code, to detect business-logic failures. A reasonable client shape is:
if response.status_code >= 500:
# transport-level retry
elif response.status_code in (400, 401, 403, 404):
# caller bug — fix the request, not the network
else:
body = response.json()
if body['status'] == 'SUCCESS':
...
else:
handle_business_failure(body['message'])
See Errors → Overview for the full list of HTTP statuses and global message codes, and Carrier errors for the carrier-originated message catalog.