Skip to main content

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:

FieldTypeMeaning
status"SUCCESS" | "FAIL"Coarse outcome. Branch on this field to detect business-logic failures.
messagestringShort machine-readable code (e.g. "INVALID_MSISDN", "CARRIER_NOT_SUPPORTED").
status_codeintegerMirrors the HTTP response status. Included for clients that only have access to the response body (e.g. behind a transport that strips HTTP).
payloadobject | nullEndpoint-specific shape on success. Omitted (or null) on failures.
warning
Important note: HTTP 200 with 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.