Skip to main content

Collection API

Author: Josh Lead Backend Team
Last Updated: 2026-03-06
Version: v1.0

Base URL: https://api.nextgate.co.tz/api/v1

Short Description: The Collection API handles wallet top-up flows for NextGate users. It allows users to deposit money into their NextGate wallet via mobile money (USSD push) or card payment through Selcom. All subsequent platform payments (events, products) are made from the wallet balance.

Hints:

  • Always provide a unique idempotencyKey per request to prevent duplicate charges
  • For USSD channels the user receives a PIN prompt on their phone — poll /status/{id} to track completion
  • For CARD channel the response contains a paymentUrl — redirect the user to complete payment
  • Selcom webhooks automatically credit the wallet on confirmation — no manual step needed

Standard Response Format

Success Response

{
  "success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2026-03-06T10:30:45",
  "data": {}
}

Error Response

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2026-03-06T10:30:45",
  "data": "Error description"
}

Standard Response Fields

Field Type Description
success boolean true for success, false for errors
httpStatus string HTTP status name
message string Human-readable result description
action_time string ISO 8601 timestamp
data object/string Response payload or error details

Flow Diagram

User
 |
 |--- POST /collection/initiate --------> CollectionController
 |       (channel, amount, msisdn)                |
 |                                       Validate request
 |                                       Check idempotency
 |                                       Save CollectionRequest (PENDING)
 |                                                |
 |                               .----------------.
 |                               |                |
 |                            USSD             CARD
 |                               |                |
 |                        Push USSD to      Create Selcom
 |                        Selcom API        Checkout Order
 |                               |                |
 |                        Status: AWAITING  Status: AWAITING
 |                        CUSTOMER_ACTION   CUSTOMER_ACTION
 |                               |                |
 |<-- Response (requestId) ------'----------------'
 |
 |   [USSD: User enters PIN on phone]
 |   [CARD: User pays on paymentUrl ]
 |
 |                   Selcom Webhook ---------> /api/selcom/webhook
 |                   (payment confirmed)               |
 |                                           creditWallet()
 |                                           Status: COMPLETED
 |
 |--- GET /collection/status/{id} -------> Status: COMPLETED
 |<-- { status, transactionRef, ... }

Endpoints


1. Initiate Collection

Purpose: Initiates a wallet top-up request via mobile money (USSD) or card payment.

Endpoint: POST /collection/initiate

Access Level: 🔒 Protected — Requires Bearer Token

Authentication: Authorization: Bearer <token>

Request Headers:

Header Type Required Description
Authorization string Yes Bearer JWT token
Content-Type string Yes application/json

Request JSON Sample (USSD):

{
  "channel": "MPESA",
  "amount": 50000,
  "msisdn": "255712345678",
  "idempotencyKey": "usr-123-topup-1741234567"
}

Request JSON Sample (CARD):

{
  "channel": "CARD",
  "amount": 50000,
  "idempotencyKey": "usr-123-topup-1741234568"
}

Request Body Parameters:

Parameter Type Required Description Validation
channel string Yes Payment channel enum: MPESA, AIRTEL, TIGO, HALOPESA, SELCOM_PESA, CARD
amount number Yes Amount to top up in TZS Min: 1000
msisdn string Conditional Mobile phone number Required for all channels except CARD. Format: 255XXXXXXXXX (12 digits)
idempotencyKey string Yes Unique key to prevent duplicate requests Max 200 chars, unique per request

Success Response JSON Sample (USSD):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Collection initiated successfully",
  "action_time": "2026-03-06T10:30:45",
  "data": {
    "collectionRequestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "channel": "MPESA",
    "amount": 50000,
    "currency": "TZS",
    "status": "AWAITING_CUSTOMER_ACTION",
    "msisdnDisplay": "2557****678",
    "paymentUrl": null,
    "message": "Please enter your PIN on your phone to complete payment."
  }
}

Success Response JSON Sample (CARD):

{
  "success": true,
  "httpStatus": "OK",
  "message": "Collection initiated successfully",
  "action_time": "2026-03-06T10:30:45",
  "data": {
    "collectionRequestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "channel": "CARD",
    "amount": 50000,
    "currency": "TZS",
    "status": "AWAITING_CUSTOMER_ACTION",
    "msisdnDisplay": null,
    "paymentUrl": "https://checkout.selcom.net/pay/abc123",
    "message": "Redirect user to payment URL."
  }
}

Success Response Fields:

Field Description
collectionRequestId UUID — use this to poll /status/{id}
channel Channel used for this collection
amount Amount in TZS
currency Always TZS
status Current status — see status table below
msisdnDisplay Masked phone number e.g. 2557****678
paymentUrl Card payment URL — redirect user here (null for USSD)
message Human-readable instruction for the user

Collection Status Values:

Status Description
PENDING Request created, not yet sent to Selcom
AWAITING_CUSTOMER_ACTION Sent to Selcom, waiting for user PIN or card payment
COMPLETED Payment confirmed, wallet credited
FAILED Payment failed or rejected
EXPIRED Request expired before user acted (30 min window)

Error Responses:

Missing phone for USSD (400):

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Phone number is required for MPESA payments.",
  "action_time": "2026-03-06T10:30:45",
  "data": "Phone number is required for MPESA payments."
}

Invalid phone format (400):

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Invalid phone number format.",
  "action_time": "2026-03-06T10:30:45",
  "data": "Invalid phone number format."
}

Selcom rejection (400):

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Payment initiation failed: Subscriber not found",
  "action_time": "2026-03-06T10:30:45",
  "data": "Payment initiation failed: Subscriber not found"
}

2. Get Collection Status

Purpose: Returns the current status of a collection request. Poll this after initiating to know when the wallet has been credited.

Endpoint: GET /collection/status/{collectionRequestId}

Access Level: 🔒 Protected — Requires Bearer Token

Authentication: Authorization: Bearer <token>

Path Parameters:

Parameter Type Required Description
collectionRequestId UUID Yes ID returned from /initiate

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Collection status retrieved",
  "action_time": "2026-03-06T10:30:45",
  "data": {
    "collectionRequestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "channel": "MPESA",
    "amount": 50000,
    "currency": "TZS",
    "status": "COMPLETED",
    "msisdnDisplay": "2557****678",
    "failureReason": null,
    "transactionRef": "NXT-TXN-20260306-ABCD1234",
    "createdAt": "2026-03-06T10:30:00",
    "completedAt": "2026-03-06T10:31:45"
  }
}

Success Response Fields:

Field Description
collectionRequestId UUID of this collection request
channel Channel used
amount Amount in TZS
currency Always TZS
status Current status — see status table above
msisdnDisplay Masked phone number
failureReason Populated if status is FAILED
transactionRef Wallet transaction reference — available when COMPLETED
createdAt When the request was created
completedAt When the wallet was credited — null if not yet completed

Error Responses:

Not found or not owned by user (400):

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Collection request not found",
  "action_time": "2026-03-06T10:30:45",
  "data": "Collection request not found"
}

Quick Reference

Method Endpoint Description
POST /collection/initiate Start a top-up via USSD or card
GET /collection/status/{id} Check top-up status