API Response Contracts

This document describes the standard response format and handling rules for the API. It explains how to interpret HTTP and business status codes, how to reliably process responses, and how to implement robust client handling logic. It also provides code examples for common programming languages.

Got it — here’s the tightened, client‑friendly version of your live API documentation. HTTP 401, 403, and 500 are the only non‑200 statuses you will encounter, so clients should not expect other HTTP error codes. All business errors are always returned with HTTP 200 to clearly distinguish them from transport-level errors.

Summary of Supported HTTP Status Codes

HTTP StatusMeaningTypical CauseSuggested Client Action
200OKRequest processed successfully; check statusCode for business outcomeUse data if statusCode = "00"; otherwise follow business error guidance
401UnauthorizedMissing or invalid authenticationRe-authenticate and retry
403ForbiddenAuthenticated but not permitted to access the resourceCheck permissions or contact support
500Internal Server ErrorUnexpected backend failureRetry with exponential backoff

Note: Non-200 responses (401, 403, 500) may not always include the standard JSON body. Business errors will always be returned with HTTP 200 and the standard JSON structure.


API Response Contract & Handling Guide

Standard Response Format

{
  "data": {
    "balance": 3470577.33,
    "currency": "NGN"
  },
  "statusCode": "00",
  "message": "Successful"
}

Contract: All endpoints return data, statusCode, and message. Always check statusCode to determine the business outcome ("00" = success`). HTTP status codes indicate the transport-level result (e.g., request/connection/auth). Business status codes inside the body indicate the application-level outcome.

Versioning Note: This format is part of the stable API contract and will remain unchanged for all v1.x releases. Any breaking change will occur only in a major version (v2, v3, …) and will be announced in documentation. Clients should parse defensively to handle additive fields.


Quick Start

Test Call (curl)

curl -X GET "https://api.example.com/v1/wallet/balance" \
  -H "Authorization: Bearer <token>"

Successful Response

{
  "data": { "balance": 3470577.33, "currency": "NGN" },
  "statusCode": "00",
  "message": "Successful"
}

Business Error Response

{
  "data": null,
  "statusCode": "02",
  "message": "Insufficient wallet balance"
}

Field Semantics

  • data — Object or null.
  • statusCode — String business code.
  • message — Human‑readable description.

Handling Logic

if (HTTP status is 200) {
  if (statusCode == "00") use data
  else handle business error
} else {
  handle transport error
}

Business Error Codes

CodeMessageGuidance
00OKSuccess; use data.
01Invalid requestFix request and retry.
02Insufficient Wallet balanceTop up or reduce amount.
04Duplicate referenceUse unique reference.
10Invalid amountAdjust and retry.
24Server errorRetry with backoff.
27Product not foundVerify ID/code.
28Product not availableOut of stock/unavailable.
40Order not foundCheck identifiers.

Reliability & Retries

  • Use exponential backoff for HTTP 500.
  • Don’t auto‑retry business errors unless fixable.
  • Always log statusCode and message.

Client Example (JavaScript - fetch)

async function callApi(url) {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const body = await res.json();
  if (body.statusCode === '00') return body.data;
  throw new Error(`${body.statusCode}: ${body.message}`);
}

Client Example (Axios)

import axios from 'axios';
const api = axios.create({ baseURL: 'https://api.example.com', timeout: 15000 });
api.interceptors.response.use(
  (response) => {
    const body = response.data;
    if (body?.statusCode === '00') return body.data;
    const err = new Error(`${body?.statusCode}: ${body?.message}`);
    err.code = body?.statusCode;
    return Promise.reject(err);
  },
  (error) => {
    if (error.response) {
      return Promise.reject(new Error(`HTTP ${error.response.status}`));
    }
    return Promise.reject(error);
  }
);
export default api;

Client Example (Python - requests)

import requests
class TransportError(Exception):
    pass
class BusinessError(Exception):
    def __init__(self, code, message):
        super().__init__(f"{code}: {message}")
        self.code = code
        self.message = message

def call_api(url, method='GET', **kwargs):
    resp = requests.request(method, url, timeout=15, **kwargs)
    if not (200 <= resp.status_code < 300):
        raise TransportError(f"HTTP {resp.status_code}")
    body = resp.json()
    if body.get('statusCode') == '00':
        return body.get('data')
    raise BusinessError(body.get('statusCode'), body.get('message'))

How to Debug Failures

  1. Check HTTP status code.
  2. If HTTP is 200, check statusCode.
  3. Log statusCode, message, and request reference ID.
  4. For business errors, follow the “Guidance” column.
  5. For HTTP 500, apply exponential backoff.

FAQ

Q: Why is HTTP 200 but statusCode != "00"?
A: Business logic failed; check statusCode for the reason.
Q: Should I retry all failures?
A: Only retry HTTP 500 and fixable business errors per table.
Q: Will this format change?
A: Not in v1.x. Any breaking change will be announced in a new major version.


This version is safe for clients, matches the live API exactly, and reflects only the supported HTTP status codes.