Collection API
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
idempotencyKeyper 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 |