NAV Navbar
cURL PHP RUBY PYTHON

Overview

About zengapay

ZENGAPAY is a Payments Gateway service that enables businesses to receive payments from their customers via mobile money, as well as make mobile money payments to any mobile money account holder or send Mobile Airtime or Mobile Voice & Data Bundles directly to users.

ZENGAPAY offers a rich API which enables seamless integration with applications, websites, mobile apps, SMS services and any other medium through which businesses interact with their customers.

Scroll down for examples.

About the ZENGAPAY API

This is the official ZENGAPAY API documentation.

The ZENGAPAY API operates over HTTPS and uses JSON as its data format. The API is a RESTful API and utilizes HTTP methods and HTTP status codes to specify requests and responses.

We provide libraries for several languages, including PHP, Java, Ruby and Python. You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.

All responses shall be returned using JSON, however, if you are using the language libraries, the JSON responses will be converted into native, language specific objects.

Supported Operations

The API provides support for both transaction-oriented operations and non transaction-oriented operations. A transaction-oriented operation is an operation intended to result in the transfer of funds from one account to another account.

Transaction-oriented Operations

The API supports the following transaction-oriented operations. Note that these operations are also available from the Web Interface.

  1. Receiving Money
  2. Sending Money
  3. Sending Airtime
  4. Sending Voice & Data Bundles

Non Transaction-oriented Operations

The Following are the non transaction-oriented operations supported by the API:

  1. Check Transaction Status
  2. Check Account Balance
  3. Get Account Statement
  4. Get Supported Networks
  5. Get Supported Banks
  6. Manage Contacts

All the above operations are fully described in the later sections of this API Document.

Getting Started

We have production as well as test server environments. The test server environment is called sandbox.

You are advised to signup for a free account in sandbox, get your API Token by going to Settings → Developer Settings and test all your API calls on the sandbox environment before pointing your application to production.

To point an application to production, you need make only minor changes. In most cases, you simply need to change the API service URL and API TOKEN.

Sandbox Environment

The following is the API Endpoint for our sandbox environment:

Production Environment

We can't stress this enough: Please test your APIs against our sandbox environment before pointing them to production. We prefer to avoid service disruptions in production caused by untested code.

Here is the API Endpoint for our production environment:

Plugins And Libraries

As an alternative to working directly with our API you may also consider to use our:

Request/Response Format

The default response format is JSON. Requests with a message-body use plain JSON to set or update resource attributes. Successful requests will return a 200 OK or a 202 ACCEPTED HTTP status.

Some general information about responses:

Rate Limiting

All requests, whether they are authenticated or not, are subject to rate limiting. If you have reached your limit, your requests will be handled with a 429 Too Many Requests error. Burst requests are allowed. Responses contain several headers which provide information about your current rate limit status.

Pagination

Responses which return multiple items support pagination. If they do support pagination, it can be controlled with following query string parameters:

Error Handling

ZENGAPAY uses HTTP response status codes to indicate success or failure. When a request to our API is successful, The ZENGAPAY API returns an HTTP response code in the 2XX range. And when a request fails, returns an HTTP response code in the 4XX or 5XX range.

In summary:

In all cases, the response will include an appropriate status code and content-type. The body of the response will include any additional details regarding the nature of the error.

Most error responses will include a message key in the body of the response, but some may include additional information, for example, they may include information about missing fields.

The examples to the right show some of the errors that might occur, including the status code and content-type in the response header, and the json-formatted details in the response body.

401 Unauthorized error response due to using a wrong token.

{
    "code": 401,
    "status": "Unauthorized",
    "message": "The API KEY you supplied is either INVALID or API access has been DISABLED because your account is unverified. If this error persists, please contact support@zengapay.com"
}

Authentication

To authorize, use this code:


curl --header 'Authorization: Bearer <YOUR_API_TOKEN>' --location 'https://api.zengapay.com/v1/'

Make sure to replace YOUR_API_TOKEN with your API Token eg ZPYPUBK-x9cb6a2b822267b21ec4c5XXXXXXXXXXXXX

All requests to the ZENGAPAY API must be authenticated via a API token. Include your secret API token in every request you send to the API with the Authorization HTTP header.

Authorization: Bearer <YOUR_API_TOKEN>

To create a new API Token, Login into your ZENGAPAY Dashboard, go to Settings → Developer Settings, and create a new Key.

An Example of Authorization in Postman

Authentication

IP Whitelisting

You should whitelist at least one IP before your API access will fully begin to work.

Only requests from whitelisted IP addresses will be allowed access to security sensitive actions such as Transfers, Airtime & Data Transfers.

IP Address(es) can be whitelisted in the following way:

  1. Login into your ZENGAPAY Dashboard,

  2. Go to Settings → Developer Settings.

  3. Scroll down to the IP Address(es) section.

  4. Add one or more IP Address(es) separated by commas. eg 95.217.165.253,88.198.95.21

  5. Proceed to Save Changes.

Please keep in mind that only IPv4 addresses can be used and the IP must be your SERVER's or ISP's Public IP.

Protected Endpoint that requires Whitelisted IPs

{
    "code": 403,
    "status": "Forbidden",
    "message": "Connection refused from unauthorized IP: 81.202.241.36,  This IP is NOT unauthorized to access the /account endpoint on your account. If you believe this is in error, please contact support@zengapay.com"
}

Transaction Statuses

Transaction-oriented Operations such as collections, payouts and internal transfers will return one of these transaction statuses FAILED, PENDING,SUCCEEDED or INDETERMINATE.

The diagram below attempts to provide a visual explanation of the transaction statuses supported in the system.

Transaction statuses

  1. FAILED - This means that your transaction was not successful. You may re-submit your request for processing if there was an error on your part.

  2. PENDING - This means that your transaction has not yet been processed to a conclusion.

  3. SUCCEEDED - This means that your transaction was Successful

  4. INDETERMINATE - This means that your transaction is pending resolution. This normally happens if there was a delay in processing of mobile money transactions. Typically requests which result in this status are resolved within 24 hours of your initiating the request.

Receiving Funds

Introduction

ZENGAPAY uses the term Collections to refer to money that you receive (or collect) from a mobile subscriber. This differentiates money you receive (Collections) from money you send to mobile subscribers (Transfers).

Collections

Use this to deposit funds into your account by transferring the said funds from a mobile money account holder. An example of where this transaction is useful is when receiving payment from a mobile money user for services you are rendering to them.

Shortly after you submit your request, the mobile money user receives an on-screen notification on their mobile phone. This notification informs the mobile money user about your request to transfer funds out of their account, and requests them to authorize the request to complete the transaction. The transaction will not succeed unless the mobile money user authorizes it through the onscreen notification.

The collection.success and collection.failed events are triggered whenever something happens to a collection that you have initiated. For example, if it is successfully delivered or if it fails. You can configure a web link to receive notifications whenever this occurs. This will allow you to respond automatically whenever a collection is completed or whenever it fails.

See our Webhooks API documentation for more information.

Collections Endpoint

Sample Collection Request Object (JSON):


   curl --location --request POST 'https://api.zengapay.com/v1/collections' \
   --header 'Authorization: Bearer <YOUR_API_TOKEN>' \
   --header 'Content-Type: application/json' \
   --data-raw '{
    "msisdn":"256775203801",
    "amount":256600,
    "external_reference":"400000",
    "narration":"Credit Note - 11200390191"}'


<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://api.zengapay.com/v1/collections",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS =>"{\"msisdn\":\"256775203801\",\"amount\":256600,\"external_reference\":\"400000\",\"narration\":\"Credit Note - 11200390191\"\n}",
  CURLOPT_HTTPHEADER => array(
    "Authorization: Bearer <YOUR_API_TOKEN>",
    "Content-Type: application/json"
  ),
));

$response = curl_exec($curl);
print_r($response);


  require "uri"
  require "net/http"

  url = URI("https://api.zengapay.com/v1/collections")

  https = Net::HTTP.new(url.host, url.port);
  https.use_ssl = true

  request = Net::HTTP::Post.new(url)
  request["Authorization"] = "Bearer <YOUR_API_TOKEN>"
  request["Content-Type"] = "application/json"
  request.body = "{\"msisdn\":\"256775203801\",\"amount\":256600,\"external_reference\":\"400000\",\"narration\":\"Credit Note - 11200390191\"\n}"

  response = https.request(request)
  puts response.read_body


import requests

url = "https://api.zengapay.com/v1/collections"

payload = "{\"msisdn\":\"256775203801\",\"amount\":256600,\"external_reference\":\"400000\",\"narration\":\"Credit Note - 11200390191\"\n}"
headers = {
  'Authorization': 'Bearer <YOUR_API_TOKEN>',
  'Content-Type': 'application/json'
}

response = requests.request("POST", url, headers=headers, data = payload)

print(response.text.encode('utf8'))

The Collection Request object

Request in Postman

Collection request

Parameter Type Description
msisdn Integer (Required) The phone number that the collection request is intended for. Provide an internationally formatted number without the + . for example 256775203801 for MTN Uganda or 256755329361 for Airtel Uganda
amount Decimal (Required) The collection request amount. eg 186600 or 186600.00
external_reference String (Required) Internal description or reason for this collection request and must be unique for every request. For example, this may be an invoice number or an internal transaction identifier in your system. eg 000000018889191
narration String (Required) Textual narrative describing the transaction. eg Clearing Invoice - #0000001889191

The above command returns JSON structured like this:

{
    "code": 202,
    "status": "accepted",
    "message": "Transaction Initiated",
    "transactionReference": "48f4be7a-73b3-524d-9a98-6290b56c5f2e"
}

Response sample in Postman

Collection request response

Retrieving a single Collection (Checking Transaction Status)

To retrieve a single collection object (check status of a collection request), provide the transactionReference and a collection object will be returned.

Request Parameters

Parameter Type Description
transactionReference UUID The reference to the transaction whose status you would like to follow up on. This is typically the transaction reference which came through as part of an earlier collection request response.

Sample Request:


curl --location --request GET 'https://api.zengapay.com/v1/collections/ab1df360-3191-5e8a-acd6-c65281f22e9f' \
--header 'Authorization: Bearer <YOUR_API_TOKEN>'


<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://api.zengapay.com/v1/collections/ab1df360-3191-5e8a-acd6-c65281f22e9f",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => array(
    "Authorization: Bearer <YOUR_API_TOKEN>"
  ),
));

$response = curl_exec($curl);
print_r($response);


require "uri"
require "net/http"

url = URI("https://api.zengapay.com/v1/collections/ab1df360-3191-5e8a-acd6-c65281f22e9f")

https = Net::HTTP.new(url.host, url.port);
https.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = "Bearer <YOUR_API_TOKEN>"

response = https.request(request)
puts response.read_body


import requests

url = "https://api.zengapay.com/v1/collections/ab1df360-3191-5e8a-acd6-c65281f22e9f"

payload = {}
headers = {
  'Authorization': 'Bearer <YOUR_API_TOKEN>'
}

response = requests.request("GET", url, headers=headers, data = payload)

print(response.text.encode('utf8'))

Sample Response

The gateway will respond back with an object with this structure:

Response in displayed in Postman

Collection status response

Response Parameters

Parameter Type Description
transactionStatus string The status of the transaction, this can be one of these; PENDING,SUCCEEDED,FAILED,INDETERMINATE
transactionReference string The reference to the transaction requested
MNOTransactionReferenceId string Mobile Network Operator (MNO) Transaction Reference Identifier. The value uniquely identifies this transaction on the relevant Mobile Network Operator's system.
currencyCode string The currency code of the wallet currency in your merchant account where the amount paid by the customer will be deposited.
msisdn string The mobile money account (phone) from where you are deducting the funds.
amount string Amount requested for collection
channel string The Channel through which the transaction was initiated. this can be one of these; API,WEB,APP
transactionType string The transaction type. for collections this is usually INBOUND_MSISDN_CREDIT
transactionInitiationDate string The transaction initiation date. This is of the format YYYY-MM-DD HH:mm:ss, e.g 2020-05-15 18:44:44
transactionCompletionDate string The transaction completion date. This is of the format YYYY-MM-DD HH:mm:ss, e.g 2020-05-15 18:44:55. If the transaction is not yet complete, this will be set to 0000-00-00 00:00:00

Response:

{
    "code": 200,
    "status": "OK",
    "data": {
        "transactionSystemId": "ZPY210126134940TX",
        "transactionReference": "4ff14e2a-27d5-5287-8783-46587e45f227",
        "transactionStatus": "SUCCEEDED",
        "amount": 176650,
        "msisdn": "256755329361",
        "channel": "API",
        "customerCharged": false,
        "currencyCode": "UGX-ATLMM",
        "currencyName": "Ugandan shillings (Airtel Money)",
        "MNOTransactionReferenceId": "49747002508",
        "transactionExternalReference": "21012616232830",
        "transactionExternalNarrative": "Clearing Invoice - 2101254558",
        "transactionInitiationDate": "2021-01-26 16:23:38",
        "transactionCompletionDate": "2021-01-26 16:23:56",
        "transactionEntryGeneralType": "CREDIT",
        "transactionEntryDesignation": "TRANSACTION",
        "transactionEntrySpecificType": "INBOUND_MSISDN_CREDIT"
    }
}

Retrieving all Collections

To retrieve a list of all collections, make a GET request to the collections endpoint. This will return a list of collection objects.

Response


{
    "code": 200,
    "status": "OK",
    "collections": {
        "current_page": 24,
        "data": [
            {
                "transactionSystemId": "ZPY210125192181TX",
                "transactionReference": "15ebb100-61fe-5a21-a3dd-990104284c11",
                "transactionStatus": "SUCCEEDED",
                "amount": 3000,
                "msisdn": "256755329361",
                "channel": "API",
                "customerCharged": false,
                "currencyCode": "UGX-ATLMM",
                "currencyName": "Ugandan shillings (Airtel Money)",
                "MNOTransactionReferenceId": "49724030274",
                "transactionExternalReference": "21012522574267",
                "transactionExternalNarrative": "Credit Note - 2101253783",
                "transactionInitiationDate": "2021-01-25 22:57:42",
                "transactionCompletionDate": "2021-01-25 22:57:52",
                "transactionEntryGeneralType": "CREDIT",
                "transactionEntryDesignation": "TRANSACTION",
                "transactionEntrySpecificType": "INBOUND_MSISDN_CREDIT"
            },
            {
                "transactionSystemId": "ZPY210126134940TX",
                "transactionReference": "4ff14e2a-27d5-5287-8783-46587e45f227",
                "transactionStatus": "SUCCEEDED",
                "amount": 176650,
                "msisdn": "256755329361",
                "channel": "API",
                "customerCharged": false,
                "currencyCode": "UGX-ATLMM",
                "currencyName": "Ugandan shillings (Airtel Money)",
                "MNOTransactionReferenceId": "49747002508",
                "transactionExternalReference": "21012616232830",
                "transactionExternalNarrative": "Clearing Invoice - 2101254558",
                "transactionInitiationDate": "2021-01-26 16:23:38",
                "transactionCompletionDate": "2021-01-26 16:23:56",
                "transactionEntryGeneralType": "CREDIT",
                "transactionEntryDesignation": "TRANSACTION",
                "transactionEntrySpecificType": "INBOUND_MSISDN_CREDIT"
            },
            {
                "transactionSystemId": "ZPY210126171198TX",
                "transactionReference": "4a7779f3-1575-5e3b-9561-48fbcab0bc76",
                "transactionStatus": "SUCCEEDED",
                "amount": 7450,
                "msisdn": "256755329361",
                "channel": "API",
                "customerCharged": false,
                "currencyCode": "UGX-ATLMM",
                "currencyName": "Ugandan shillings (Airtel Money)",
                "MNOTransactionReferenceId": "49758316690",
                "transactionExternalReference": "21012620004480",
                "transactionExternalNarrative": "Credit Note - 2101266096",
                "transactionInitiationDate": "2021-01-26 20:00:44",
                "transactionCompletionDate": "2021-01-26 20:00:54",
                "transactionEntryGeneralType": "CREDIT",
                "transactionEntryDesignation": "TRANSACTION",
                "transactionEntrySpecificType": "INBOUND_MSISDN_CREDIT"
            }
        ],
        "first_page_url": "https://api.zengapay.com/v1/collections?page=1",
        "from": 576,
        "last_page": 24,
        "last_page_url": "https://api.zengapay.com/v1/collections?page=24",
        "links": [
            {
                "url": "https://api.zengapay.com/v1/collections?page=23",
                "label": "pagination.previous",
                "active": false
            },
            {
                "url": "https://api.zengapay.com/v1/collections?page=1",
                "label": 1,
                "active": false
            },
            {
                "url": "https://api.zengapay.com/v1/collections?page=2",
                "label": 2,
                "active": false
            },
            {
                "url": null,
                "label": "...",
                "active": false
            },
            {
                "url": "https://api.zengapay.com/v1/collections?page=24",
                "label": 24,
                "active": true
            },
            {
                "url": null,
                "label": "pagination.next",
                "active": false
            }
        ],
        "next_page_url": null,
        "path": "https://api.zengapay.com/v1/collections",
        "per_page": 25,
        "prev_page_url": "https://api.zengapay.com/v1/collections?page=23",
        "to": 580,
        "total": 580
    }
}

Sending Money

Introduction

ZENGAPAY uses the term Transfers to refer to money that you send to a mobile subscriber. This differentiates money you send to mobile subscribers (aka Transfers) from money you receive from mobile subscribers (aka Collections).

ZENGAPAY provides the Transfers API to enable you to send money to a mobile subscriber, withdraw your funds, or to view transfers that you have previously sent to a mobile subscriber.

See the Transfers documentation below for more information.

The transfer.success and transfer.failed events are triggered whenever something happens to a transfer that you have initiated. For example, if it is successfully delivered or if it fails. You can configure a web link to receive notifications whenever this occurs. This will allow you to respond automatically whenever a transfer is completed or whenever it fails.

See our Webhooks API documentation for more information.

Transfers

To transfer money (mobile money) to a mobile subscriber, you create a new transfer object using the transfers API. You can also use the transfers API to retrieve individual transfers or list all transfers, as shown in the sections below.

The transfers api endpoint is:

Sample Transfer Request Object (JSON):


   curl --location --request POST 'https://api.zengapay.com/v1/transfers' \
   --header 'Authorization: Bearer <YOUR_API_TOKEN>' \
   --header 'Content-Type: application/json' \
   --data-raw '{
    "msisdn":"256775203801",
    "amount":256600,
    "external_reference":"400000",
    "narration":"Payout - 11200390191"}'


<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://api.zengapay.com/v1/transfers",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS =>"{\"msisdn\":\"256775203801\",\"amount\":256600,\"external_reference\":\"400000\",\"narration\":\"Payout - 11200390191\"\n}",
  CURLOPT_HTTPHEADER => array(
    "Authorization: Bearer <YOUR_API_TOKEN>",
    "Content-Type: application/json"
  ),
));

$response = curl_exec($curl);
print_r($response);


  require "uri"
  require "net/http"

  url = URI("https://api.zengapay.com/v1/transfers")

  https = Net::HTTP.new(url.host, url.port);
  https.use_ssl = true

  request = Net::HTTP::Post.new(url)
  request["Authorization"] = "Bearer <YOUR_API_TOKEN>"
  request["Content-Type"] = "application/json"
  request.body = "{\"msisdn\":\"256775203801\",\"amount\":256600,\"external_reference\":\"400000\",\"narration\":\"Payout - 11200390191\"\n}"

  response = https.request(request)
  puts response.read_body


import requests

url = "https://api.zengapay.com/v1/transfers"

payload = "{\"msisdn\":\"256775203801\",\"amount\":256600,\"external_reference\":\"400000\",\"narration\":\"Payout - 11200390191\"\n}"
headers = {
  'Authorization': 'Bearer <YOUR_API_TOKEN>',
  'Content-Type': 'application/json'
}

response = requests.request("POST", url, headers=headers, data = payload)

print(response.text.encode('utf8'))

The Transfer Request object

Request in Postman

Transfer request

Parameter Type Description
msisdn Integer (Required) The phone number that the collection request is intended for. Provide an internationally formatted number without the + . for example 256775203801 for MTN Uganda or 256755329361 for Airtel Uganda
amount Decimal (Required) The amount you want to transfer or payout eg 186600 or 186600.00
external_reference String (Required) Internal description or reason for this transfer request and must be unique for every request. For example, this may be an internal transaction identifier in your system. eg 000000018889191
narration String (Required) Textual narrative describing the transfer. eg Payout - #0000001889191

The above command returns JSON structured like this:

{
    "code": 202,
    "status": "accepted",
    "message": "Transaction Initiated",
    "transactionReference": "48f4be7a-73b3-524d-9a98-6290b56c5f2e"
}

Response sample in Postman

Transfer request response

Retrieving a single Transfer (Verify Transfer Status)

To retrieve a single transfer object (verify status of a collection request), provide the transactionReference and a transfer object will be returned.

Request Parameters

Parameter Type Description
transactionReference UUID The reference to the transaction whose status you would like to follow up on. This is typically the transaction reference which came through as part of an earlier transfer request response.

Sample Request:


curl --location --request GET 'https://api.zengapay.com/v1/transferss/ab1df360-3191-5e8a-acd6-c65281f22e9f' \
--header 'Authorization: Bearer <YOUR_API_TOKEN>'


<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://api.zengapay.com/v1/transfers/ab1df360-3191-5e8a-acd6-c65281f22e9f",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => array(
    "Authorization: Bearer <YOUR_API_TOKEN>"
  ),
));

$response = curl_exec($curl);
print_r($response);


require "uri"
require "net/http"

url = URI("https://api.zengapay.com/v1/transfers/ab1df360-3191-5e8a-acd6-c65281f22e9f")

https = Net::HTTP.new(url.host, url.port);
https.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = "Bearer <YOUR_API_TOKEN>"

response = https.request(request)
puts response.read_body


import requests

url = "https://api.zengapay.com/v1/transfers/ab1df360-3191-5e8a-acd6-c65281f22e9f"

payload = {}
headers = {
  'Authorization': 'Bearer <YOUR_API_TOKEN>'
}

response = requests.request("GET", url, headers=headers, data = payload)

print(response.text.encode('utf8'))

Sample Response

The gateway will respond back with an object with this structure:

Response in displayed in Postman

Transfer get response

Response Parameters

Parameter Type Description
transactionStatus string The status of the transaction, this can be one of these; PENDING,SUCCEEDED,FAILED,INDETERMINATE
transactionReference string The reference to the transaction requested
MNOTransactionReferenceId string Mobile Network Operator (MNO) Transaction Reference Identifier. The value uniquely identifies this transaction on the relevant Mobile Network Operator's system.
currencyCode string The currency code of the wallet currency in your merchant account where the amount paid by the customer will be deposited.
msisdn string The mobile money account (phone number) where you are transferring the money to.
amount string Amount requested for transfer
channel string The Channel through which the transaction was initiated. this can be one of these; API,WEB,APP
transactionType string The transaction type. for transfers this is usually OUTBOUND_MSISDN_CREDIT
transactionInitiationDate string The transaction initiation date. This is of the format YYYY-MM-DD HH:mm:ss, e.g 2020-05-15 18:44:44
transactionCompletionDate string The transaction completion date. This is of the format YYYY-MM-DD HH:mm:ss, e.g 2020-05-15 18:44:55. If the transaction is not yet complete, this will be set to 0000-00-00 00:00:00

Response:

{
    "code": 200,
    "status": "OK",
    "data": {
        "transactionSystemId": "ZPY201011098363TX",
        "transactionReference": "950ec58b-8b90-5219-aecc-41520f7e540e",
        "transactionStatus": "SUCCEEDED",
        "amount": 3200,
        "msisdn": "256755329361",
        "channel": "API",
        "currencyCode": "UGX-ATLMM",
        "currencyName": "Ugandan shillings (Airtel Money)",
        "MNOTransactionReferenceId": "44970884037",
        "transactionExternalReference": "ENCPT730223314",
        "transactionExternalNarrative": "Encapto MYUG 3k",
        "transactionInitiationDate": "2020-10-11 12:44:04",
        "transactionCompletionDate": "2020-10-11 12:44:09",
        "transactionEntryGeneralType": "DEBIT",
        "transactionEntryDesignation": "TRANSACTION",
        "transactionEntrySpecificType": "OUTBOUND_MSISDN_DEBIT"
    }
}

Retrieving all Transfers

To retrieve a list of all transfers, make a GET request to the collections endpoint. This will return a list of collection objects.

Response


{
    "code": 200,
    "status": "OK",
    "transfers": {
        "current_page": 1,
        "data": [
            {
                "transactionSystemId": "ZPY200709074078TX",
                "transactionReference": "4682c4c8-3180-50a3-8019-f77d1be6d778",
                "transactionStatus": "SUCCEEDED",
                "amount": 2700,
                "msisdn": "256702983281",
                "channel": "API",
                "currencyCode": "UGX-ATLMM",
                "currencyName": "Ugandan shillings (Airtel Money)",
                "MNOTransactionReferenceId": "40879110750",
                "transactionExternalReference": "32020490987",
                "transactionExternalNarrative": "Payout June Salaries - BATCH-001",
                "transactionInitiationDate": "2020-07-09 07:35:40",
                "transactionCompletionDate": "2020-07-14 12:50:14",
                "transactionEntryGeneralType": "DEBIT",
                "transactionEntryDesignation": "TRANSACTION",
                "transactionEntrySpecificType": "OUTBOUND_MSISDN_DEBIT"
            },
            {
                "transactionSystemId": "ZPY200709074741TX",
                "transactionReference": "e72bbb18-46c3-507e-9219-853af7945153",
                "transactionStatus": "SUCCEEDED",
                "amount": 2000,
                "msisdn": "256702983281",
                "channel": "API",
                "currencyCode": "UGX-ATLMM",
                "currencyName": "Ugandan shillings (Airtel Money)",
                "MNOTransactionReferenceId": "40879505122",
                "transactionExternalReference": "32020490987",
                "transactionExternalNarrative": "Payout June Salaries - BATCH-001",
                "transactionInitiationDate": "2020-07-09 07:45:17",
                "transactionCompletionDate": "2020-07-14 12:50:14",
                "transactionEntryGeneralType": "DEBIT",
                "transactionEntryDesignation": "TRANSACTION",
                "transactionEntrySpecificType": "OUTBOUND_MSISDN_DEBIT"
            },
            {
                "transactionSystemId": "ZPY200810094152TX",
                "transactionReference": "e07dcc6d-adda-5304-85b3-8bde22b35dac",
                "transactionStatus": "SUCCEEDED",
                "amount": 120000,
                "msisdn": "256755329361",
                "channel": "API",
                "currencyCode": "UGX-ATLMM",
                "currencyName": "Ugandan shillings (Airtel Money)",
                "MNOTransactionReferenceId": "42241968168",
                "transactionExternalReference": "200810223314",
                "transactionExternalNarrative": "MTN Airtime Stock Purchase",
                "transactionInitiationDate": "2020-08-10 12:51:06",
                "transactionCompletionDate": "2020-08-10 12:51:09",
                "transactionEntryGeneralType": "DEBIT",
                "transactionEntryDesignation": "TRANSACTION",
                "transactionEntrySpecificType": "OUTBOUND_MSISDN_DEBIT"
            }

        ],
        "first_page_url": "https://api.zengapay.com/v1/transfers?page=1",
        "from": 1,
        "last_page": 1,
        "last_page_url": "https://api.zengapay.com/v1/transfers?page=1",
        "links": [
            {
                "url": null,
                "label": "pagination.previous",
                "active": false
            },
            {
                "url": "https://api.zengapay.com/v1/transfers?page=1",
                "label": 1,
                "active": true
            },
            {
                "url": null,
                "label": "pagination.next",
                "active": false
            }
        ],
        "next_page_url": null,
        "path": "https://api.zengapay.com/v1/transfers",
        "per_page": 25,
        "prev_page_url": null,
        "to": 24,
        "total": 24
    }
}

Airtime

Sending Airtime

Voice & Data Bundles

Sending Voice or Data Bundle

Account

Account Summary

The accounts API method allows you to get a summary of your account.

Returned Rata


{
    "code": 200,
    "status": "OK",
    "data": {
        "account": [
            {
                "name": "MOMENTUM HOSTING LIMITED",
                "number": "100835XXXXXX",
                "category": "Business",
                "address": "GPO Building, Kampala Road",
                "official_email": "billing@xxxxxx.tld",
                "support_email": "support@xxxxxxx.tld",
                "website": "https://xxxxxxxxx.tld",
                "created": "2020-06-10 22:02:51"
            }
        ],
        "balance": {
            "currency": "UGX",
            "available": 26194436.5,
            "inWords": "Twenty Six Million, One Hundred Ninety Four Thousand, Four Hundred Thirty Six Shillings"
        }
    }
}

Account Balance

This Endpoint allows you to get the current break down of your merchant account balance.

The balance api endpoint is:

Returned Data

{
    "code": 200,
    "status": "OK",
    "data": {
        "balances": [
            {
                "currencyName": "Ugandan shillings (MTN Mobile Money)",
                "currencyCode": "UGX-MTNMM",
                "currencyProvider": "MTN Uganda Limited",
                "available": 4806710
            },
            {
                "currencyName": "Ugandan shillings (Airtel Money)",
                "currencyCode": "UGX-ATLMM",
                "currencyProvider": "Airtel Uganda Limited",
                "available": 21387700
            }
        ],
        "balance": {
            "available": 26194436.5,
            "currency": "Ugandan shillings (UGX)",
            "inWords": "Twenty Six Million, One Hundred Ninety Four Thousand, Four Hundred Thirty Six Shillings"
        }
    }
}

Account Statement

To retrieve a list of all transactions on your account (account statement), make a GET request to the statements end point. This will return a list of transactions.

The Account Statement api endpoint is:

Filtering Transactions

You can search or filter your statement using the following optional filters. Simply add them to your request as shown in the examples. You can also combine multiple filters.

Note that filters return exact matches only.

Filter Type Description
start datetime The earliest date you want to include transactions from, inclusive in the format YYYY-MM-DD HH:MM:SS
end datetime The latest date you want to include transactions, inclusive in the format inclusive in the format YYYY-MM-DD HH:MM:SS
status string Transaction Status. This can be FAILED, PENDING, INDETERMINATE, SUCCEEDED
currency_code string This can be UGX-MTNMM for MTN or UGX-ATLMM for Airtel.
limit int Total Number of transactions to be returned. eg 10. Default limit is 15
designation string Transaction entry Designation. This can be TRANSACTION or CHARGES.

Request:


#Without filters (params)

curl --location --request GET 'https://api.zengapay.com/v1/account/statement' \
--header 'Authorization: Bearer <YOUR_API_TOKEN>'

#With Filters(params)

curl --location --request GET 'https://api.zengapay.com/v1/account/statement?start=2020-06-14 00:00:00&limit=5&end=2020-06-24 23:59:59' \
--header 'Authorization: Bearer <YOUR_API_TOKEN>'

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://api.zengapay.com/v1/account/statement",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => array(
    "Authorization: Bearer <YOUR_API_TOKEN>"
  ),
));

$response = curl_exec($curl);
print_r($response);


require "uri"
require "net/http"

url = URI("https://api.zengapay.com/v1/account/statement")

https = Net::HTTP.new(url.host, url.port);
https.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = "Bearer <YOUR_API_TOKEN>"

response = https.request(request)
puts response.read_body


import requests

url = "https://api.zengapay.com/v1/account/statement"

payload = {}
headers = {
  'Authorization': 'Bearer <YOUR_API_TOKEN>'
}

response = requests.request("GET", url, headers=headers, data = payload)

print(response.text.encode('utf8'))

Response


{
    "code": 200,
    "status": "OK",
    "statement": {
        "current_page": 1,
        "data": [
            {
                "transactionSystemId": "ZPY200806082352TX",
                "transactionReference": "6f32e807-2973-55a3-8a48-d4d1732311c1",
                "transactionStatus": "SUCCEEDED",
                "amount": 256600,
                "msisdn": "256755329361",
                "channel": "API",
                "currencyCode": "UGX-ATLMM",
                "currencyName": "Ugandan shillings (Airtel Money)",
                "balanceBeforeTransaction": 4794120,
                "balanceAfterTransaction": 5050720,
                "transactionExternalReference": "20080611265235",
                "transactionExternalNarrative": "Service Setup - dohgov.co",
                "transactionInitiationDate": "2020-08-06 11:26:53",
                "transactionCompletionDate": "2020-08-06 11:27:04",
                "transactionEntryGeneralType": "CREDIT",
                "transactionEntryDesignation": "TRANSACTION",
                "transactionEntrySpecificType": "INBOUND_MSISDN_CREDIT"
            },
            {
                "transactionSystemId": "ZPY200806116741TX",
                "transactionReference": "8ae8aefe-d555-5aa9-8c6c-03af8b7cb345",
                "transactionStatus": "SUCCEEDED",
                "amount": 5132,
                "msisdn": null,
                "channel": "BACK_END_PROCESSOR",
                "currencyCode": "UGX-ATLMM",
                "currencyName": "Ugandan shillings (Airtel Money)",
                "balanceBeforeTransaction": 5050720,
                "balanceAfterTransaction": 5045580,
                "transactionExternalReference": "ZPY200806082352TX",
                "transactionExternalNarrative": "AIRTEL_UGANDA 2PC DEPOSITS CHARGE FOR TRANSACTION ID #ZPY200806082352TX",
                "transactionInitiationDate": "2020-08-06 11:27:04",
                "transactionCompletionDate": "2020-08-06 11:27:04",
                "transactionEntryGeneralType": "DEBIT",
                "transactionEntryDesignation": "CHARGES",
                "transactionEntrySpecificType": "OUTBOUND_ZPACCT_DEBIT"
            },
            {
                "transactionSystemId": "ZPY200806112526TX",
                "transactionReference": "62eb9bbc-520a-58b1-bee9-2f1da582ab64",
                "transactionStatus": "SUCCEEDED",
                "amount": 5000,
                "msisdn": "256755329361",
                "channel": "API",
                "currencyCode": "UGX-ATLMM",
                "currencyName": "Ugandan shillings (Airtel Money)",
                "balanceBeforeTransaction": 5045580,
                "balanceAfterTransaction": 5050580,
                "transactionExternalReference": "20080614441078",
                "transactionExternalNarrative": "Credit Note - 2008068160",
                "transactionInitiationDate": "2020-08-06 14:44:10",
                "transactionCompletionDate": "2020-08-06 14:44:23",
                "transactionEntryGeneralType": "CREDIT",
                "transactionEntryDesignation": "TRANSACTION",
                "transactionEntrySpecificType": "INBOUND_MSISDN_CREDIT"
            },
            {
                "transactionSystemId": "ZPY200806141884TX",
                "transactionReference": "e71122c3-34c5-58e2-b3a4-efa831560b52",
                "transactionStatus": "SUCCEEDED",
                "amount": 100,
                "msisdn": null,
                "channel": "BACK_END_PROCESSOR",
                "currencyCode": "UGX-ATLMM",
                "currencyName": "Ugandan shillings (Airtel Money)",
                "balanceBeforeTransaction": 5050580,
                "balanceAfterTransaction": 5050480,
                "transactionExternalReference": "ZPY200806112526TX",
                "transactionExternalNarrative": "AIRTEL_UGANDA 2PC DEPOSITS CHARGE FOR TRANSACTION ID #ZPY200806112526TX",
                "transactionInitiationDate": "2020-08-06 14:44:23",
                "transactionCompletionDate": "2020-08-06 14:44:23",
                "transactionEntryGeneralType": "DEBIT",
                "transactionEntryDesignation": "CHARGES",
                "transactionEntrySpecificType": "OUTBOUND_ZPACCT_DEBIT"
            },
            {
                "transactionSystemId": "ZPY200806176137TX",
                "transactionReference": "9122712b-6f0c-54ff-ab53-515b8d29fae4",
                "transactionStatus": "SUCCEEDED",
                "amount": 107610,
                "msisdn": "256784553576",
                "channel": "API",
                "currencyCode": "UGX-MTNMM",
                "currencyName": "Ugandan shillings (MTN Mobile Money)",
                "balanceBeforeTransaction": 1396870,
                "balanceAfterTransaction": 1504480,
                "transactionExternalReference": "20080620471018",
                "transactionExternalNarrative": "Service Setup - afcedugc.net",
                "transactionInitiationDate": "2020-08-06 20:47:10",
                "transactionCompletionDate": "2020-08-06 20:47:34",
                "transactionEntryGeneralType": "CREDIT",
                "transactionEntryDesignation": "TRANSACTION",
                "transactionEntrySpecificType": "INBOUND_MSISDN_CREDIT"
            },
            {
                "transactionSystemId": "ZPY200806205562TX",
                "transactionReference": "ac24cc4d-56b7-53ee-9070-579aa9afa8a4",
                "transactionStatus": "SUCCEEDED",
                "amount": 2152.2,
                "msisdn": null,
                "channel": "BACK_END_PROCESSOR",
                "currencyCode": "UGX-MTNMM",
                "currencyName": "Ugandan shillings (MTN Mobile Money)",
                "balanceBeforeTransaction": 1504480,
                "balanceAfterTransaction": 1502330,
                "transactionExternalReference": "ZPY200806176137TX",
                "transactionExternalNarrative": "MTN_UGANDA 2PC DEPOSITS CHARGE FOR TRANSACTION ID #ZPY200806176137TX",
                "transactionInitiationDate": "2020-08-06 20:47:35",
                "transactionCompletionDate": "2020-08-06 20:47:35",
                "transactionEntryGeneralType": "DEBIT",
                "transactionEntryDesignation": "CHARGES",
                "transactionEntrySpecificType": "OUTBOUND_ZPACCT_DEBIT"
            }
        ],
        "first_page_url": "https://api.zengapay.com/v1/account/statement?page=1",
        "from": 1,
        "last_page": 1,
        "last_page_url": "https://api.zengapay.com/v1/account/statement?page=1",
        "links": [
            {
                "url": null,
                "label": "pagination.previous",
                "active": false
            },
            {
                "url": "https://api.zengapay.com/v1/account/statement?page=1",
                "label": 1,
                "active": true
            },
            {
                "url": null,
                "label": "pagination.next",
                "active": false
            }
        ],
        "next_page_url": null,
        "path": "https://api.zengapay.com/v1/account/statement",
        "per_page": 25,
        "prev_page_url": null,
        "to": 6,
        "total": 6
    }
}

Contacts

Introduction

Contacts represent people whom you can send payments to. The Contacts API Method allows you to add, retrieve, list and update contacts in your account. Contacts are also added automatically whenever you send a payment to a new phone number.

The contacts api endpoint is:

Networks

Introduction

The Networks API Method allows you to list all the networks supported by the ZENGAPAY API, including information like country, network and currency prefixes for each network/country

The contacts api endpoint is:

Get All Networks

cURL Request:


curl --location --request GET 'https://api.zengapay.com/v1/networks' \
--header 'Authorization: Bearer <YOUR_API_TOKEN>'

Request in Postman

Networks get all

Response


{
    "code": 200,
    "status": "OK",
    "networks": {
        "current_page": 1,
        "data": [
            {
                "id": 1,
                "networkName": "MTN Uganda Limited",
                "serviceName": "MTN Mobile Money",
                "currencyName": "Ugandan shillings (MTN Mobile Money)",
                "currencyCode": "UGX-MTNMM",
                "country": "UGANDA",
                "countryIso2": "UG"
            },
            {
                "id": 2,
                "networkName": "Airtel Uganda Limited",
                "serviceName": "Airtel Money",
                "currencyName": "Ugandan shillings (Airtel Money)",
                "currencyCode": "UGX-ATLMM",
                "country": "UGANDA",
                "countryIso2": "UG"
            }
        ],
        "first_page_url": "https://api.zengapay.com/v1/networks?page=1",
        "from": 1,
        "last_page": 1,
        "last_page_url": "https://api.zengapay.com/v1/networks?page=1",
        "next_page_url": null,
        "path": "https://api.zengapay.com/v1/networks",
        "per_page": 15,
        "prev_page_url": null,
        "to": 2,
        "total": 2
    }
}

Retrieving a Single Network

To retrieve a single network, provide the network id and a network object will be returned.

Request Parameters

Parameter Type Required ? Description
id Integer Yes The id of the network you want to retrieve. eg 2 for AIRTEL UGANDA LIMITED

cURL Request:


curl --location --request GET 'https://api.zengapay.com/v1/networks/2' \
--header 'Authorization: Bearer <YOUR_API_TOKEN>'

Request in Postman

Networks get single

Response


  {
      "code": 200,
      "status": "OK",
      "network": {
          "networkName": "Airtel Uganda Limited",
          "serviceName": "Airtel Money",
          "currencyName": "Ugandan shillings (Airtel Money)",
          "currencyCode": "UGX-ATLMM",
          "country": "UGANDA",
          "countryIso2": "UG"
      }
  }

Banks

Introduction

This api allows the users to check which banks are supported by the ZENGAPAY API. The API endpoint is

The banks api endpoint is:

Webhooks

Rather than build and manage a system to verify transactions endlessly, we encourage you to embrace our "don't call us, we will call you" maxim. Handle webhooks.

Whenever actions are carried out on your ZENGAPAY Account, we trigger events which your application can hook into.

This is where webhooks come in.

You can set this up on your dashboard by specifying a URL we would send POST requests to whenever something interesting happens such as a collection or transfer.

Tips for a good webhook url

Setting a webhook

  1. Go to Account Settings

  2. Click the Developer Settings.

  3. Set the WEBHOOK URL field with your WEBHOOK URL eg. https://exmaple.org/callback

  4. Set the Secret Hash. A secret hash is used to verify your webhook requests.

  5. Enable Webhooks

Receiving a Webhook

  1. We will call your webhook url whenever the transaction status changes.
  2. You will need to acknowledge receipt of the callback by responding with http status code 200. Otherwise we will keep calling your endpoint at an interval of 5 minutes for 24 hours.

Creating an endpoint to receive webooks on your application is as easy as creating a new page that accepts unauthenticated POST requests. The webhook object is sent as JSON in the request body.

You may also use the following services to test and verify webhooks before pointing the webhook url to your servers.

  1. RequestBin
  2. Webook.site
  3. Beeceptor

Receiving a Webhook


Node.js
// Using Express
app.post("/my/webhook/url", function(req, res) {
  // Retrieve the request's body
  var event = req.body;
  // Do something with event
  res.send(200);
});


<?php
// Retrieve the request's body and parse it as JSON
$input = @file_get_contents("php://input");
$event = json_decode($input);
// Do something with $event
http_response_code(200); // PHP 5.4 or greater
?>

Confirming a webhook

  1. To ensure that the callback data sent to your webhook URL comes from our servers, you need to set a secret-hash under the API Settings.

  2. We will include a header called X-ZENGAPAY-SIGNATURE in the request sent to the callback url. e.g "X-ZENGAPAY-SIGNATURE: XXXX" which is essentially a HMAC SHA256 signature of the transaction payload (transactionReference+msisdn+amount). Yes, signed using your secret hash..

Sample PHP implementation to generate verification signature:


<?php
// only a post with ZENGAPAY signature header gets our attention
if ((strtoupper($_SERVER['REQUEST_METHOD']) != 'POST') || !array_key_exists('HTTP_X_ZENGAPAY_SIGNATURE', $_SERVER))
    exit();

// Retrieve the request's body
$input = @file_get_contents("php://input");
define('YOUR_SECRET_HASH','SECRET_HASH');   // Same as that set in your ZENGAPAY Webhook settings

// validate event do all at once to avoid timing attack
if($_SERVER['HTTP_X_ZENGAPAY_SIGNATURE'] !== hash_hmac('sha256', $input, YOUR_SECRET_HASH))
  exit();
// Where $input is concatenated string containing `transactionReference`+`msisdn`+`amount`

http_response_code(200);

// parse callback data (which is json string) as object
// Do something - that will not take long - with $data
$data = json_decode($input);

exit();

Responding to webhooks

To acknowledge receipt of a webhook, your endpoint should return a 200 HTTP status code. All response codes outside this range, including 3xx codes, will indicate to ZENGAPAY that you did not receive the webhook.

This does mean that a URL redirection or a "Not Modified" response will be treated as a failure. ZENGAPAY will ignore any other information returned in the request headers or request body.

If your endpoint does not successfully receive a webhook for any reason, webhooks would not be retried, though you can query for the status using the GET transaction endpoint to reconcile your data with any missed events.

Structure of a webhook object

A webhook object object is sent in JSON and similar to what you would get in response to a typical API request. Well, the data bit of it. Below is the body of an event that was fired when we created a collection request . The other tabs highlight the structure of other types of webhooks

collection.success Webhook

{
    "event": "collection.success",
    "data": {
        "transactionStatus": "SUCCEEDED",
        "transactionReference": "566a4a89-4024-584c-b1f5-8c59aff0d334",
        "externalTransactionReference": "20080513164357",
        "MNOTransactionReferenceId": "42026012031",
        "currencyCode": "UGX-ATLMM",
        "amount": "126600",
        "msisdn": "256755329361",
        "channel": "API",
        "transactionType": "INBOUND_MSISDN_CREDIT",
        "transactionInitiationDate": "2020-08-05 13:16:43",
        "transactionCompletionDate": "2020-08-05 13:16:55"
    }
}

Errors

The ZENGAPAY API uses the following error codes:

Code Meaning
400 Bad Request -- Your request is invalid.
401 Unauthorized -- Your API key is wrong.
403 Forbidden -- Access to the requested resource is forbidden for some reason.
404 Not Found -- The specified transaction or resource could not be found.
405 Method Not Allowed -- You tried to access an endpoint with an invalid method.
406 Not Acceptable -- You requested a format that isn't json.
422 Unprocessable Entity -- A required field was missing or invalid
429 Too Many Requests -- You have reached your API call(s) rate limit.
500 Internal Server Error -- We had a problem with our server. Try again later.
503 Service Unavailable -- We're temporarily offline for maintenance. Please try again later.