Xtopay
Payment APIs

Direct Receive Money

Prompt a customer to approve an inbound Mobile Money payment directly via API — no hosted checkout required.

Direct Receive Money

The Direct Receive Money API allows merchants to programmatically initiate a Mobile Money pull (debit prompt) directly on a customer's wallet. The customer receives an on-screen USSD or OTP authorization prompt on their mobile device to approve the charge without being redirected to a checkout webpage.

This API is ideal for in-app checkout flows, programmatic subscription renewals, and API-triggered billing systems.


Security Requirements

To prevent individuals from receiving unsolicited mobile money debit prompts (spam protection), merchants are required to implement at least one of the following security features:

  • Locked Fields for Registered Users: Ensure that only verified, registered users in your application can initiate payments. Before payment triggers, they must not be allowed to edit their pre-verified wallet numbers.
  • OTP Verification: For guest or unregistered users, send a one-time passcode (OTP) to their mobile number and require verification before triggering the payment request.

Available Channels

Xtopay supports direct mobile money collections across the following major networks:

Mobile Money ProviderChannel IdentifierCountry
MTN Mobile MoneyMTNGhana, Uganda, Côte d'Ivoire, Zambia
Telecel CashTELECELGhana
AT Money (AirtelTigo)AIRTELTIGOGhana
M-PesaMPESAKenya, Tanzania

Business IP Whitelisting

[!WARNING] Whitelisting Required Direct debit API endpoints are live and require strict IP whitelisting. Requests from non-whitelisted IP addresses will return a 403 Forbidden error or timeout.

Please submit your production server public IP addresses to your Xtopay Retail Systems Engineer for whitelisting. We permit a maximum of four (4) IP addresses per service key.


Understanding the Service Flow

The Direct Receive Money process is asynchronous. Since it requires the mobile subscriber's manual approval (via PIN entry or USSD menus) on their phone, the final transaction status is not immediately available and may take up to 30 seconds.

Service Steps

StepDescription
1Your application server makes a Direct Receive Money request to POST /v1/payments/receive.
2Xtopay performs credential authentication and returns a 200 OK or 202 Accepted response with status PROCESSING (ResponseCode 0001).
3Xtopay sends a prompt to the customer's device. The customer enters their PIN to authorize the transaction.
4Once confirmed, Xtopay dispatches an asynchronous HTTP POST payload to your registered callback URL.
5In rare instances where you do not receive the webhook callback within five (5) minutes, you must query the Transaction Status Check API (GET /v1/payments/:reference) to fetch the final state.

Direct Receive Request Flow

sequenceDiagram
    autonumber
    actor Customer
    participant AppServer as Merchant Server
    participant Xtopay as Xtopay API
    participant Telco as Telco Gateway

    Customer->>AppServer: Initiate payment action
    AppServer->>Xtopay: POST /v1/payments/receive (Auth: Bearer secret_key)
    Xtopay-->>AppServer: Return processing response (ResponseCode 0001)
    Xtopay->>Telco: Send debit instruction
    Telco-->>Customer: Trigger USSD PIN prompt / OTP on handset
    Customer->>Telco: Enter PIN / approve charge
    Telco-->>Xtopay: Authorize & process settlement
    Xtopay->>AppServer: Send Webhook Callback (POST payment.succeeded)
    Note over AppServer, Xtopay: Fallback: Check status via GET /v1/payments/:reference if callback fails

API Reference

To initiate a direct mobile money debit request, send an HTTP POST request to the endpoint below.

DetailDescription
API Endpointhttps://api.xtopay.co/v1/payments/receive
Request TypePOST
Content Typeapplication/json
AuthenticationBasic Base64(clientId:clientSecret)

Request Parameters

ParameterTypeRequiredDescription
amountIntegerYesSpecifies the total amount to charge in minor units (e.g. 5000 = GHS 50.00). Decimals are not allowed.
currencyStringNoThree-letter ISO 4217 currency code (e.g. GHS, KES, USD). Case-insensitive. Defaults to GHS.
phoneStringYesThe customer's mobile money wallet number in international format (e.g. +233249111411 or 233249111411).
providerStringYesThe network provider channel code. Available codes: MTN, TELECEL, AIRTELTIGO, MPESA.
descriptionStringYesTransaction details displayed to the user on their phone.
customerIdStringNoXtopay customer ID to link the payment.
metadataObjectNoFlat JSON key-value store to attach merchant references (e.g. { "clientReference": "3jL2KlUy3vt21" }).

Response Parameters

ParameterTypeDescription
successBooleanIndicates if the transaction request was accepted successfully.
dataObjectContains details of the initiated payment.
data.idStringUnique transaction ID generated by Xtopay.
data.referenceStringUnique transaction reference string.
data.statusStringInitial status of the debit (always PROCESSING).
data.amountIntegerAmount requested in minor units.
data.currencyStringCurrency code.
data.phoneStringIngested wallet number.
data.providerStringIngested provider code.
data.descriptionStringTransaction description.
data.createdAtStringISO 8601 timestamp.

Code Examples

curl https://api.xtopay.co/v1/payments/receive \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 80,
    "currency": "GHS",
    "phone": "233200000000",
    "provider": "TELECEL",
    "description": "Union Dues",
    "metadata": {
      "clientReference": "3jL2KlUy3vt21"
    }
  }'

Sample Response

200 OK

{
  "success": true,
  "message": "Transaction pending. Expect callback request for final state",
  "responseCode": "0001",
  "data": {
    "id": "pay_09f84e20a283942e807128e8c21d08d6",
    "reference": "3jL2KlUy3vt21",
    "status": "PROCESSING",
    "amount": 80,
    "currency": "GHS",
    "phone": "233200000000",
    "provider": "TELECEL",
    "description": "Union Dues",
    "createdAt": "2026-06-06T12:00:00Z"
  }
}

Webhook Callback

Xtopay dispatches an HTTP POST payload to your registered callback URL once the transaction is finalized by the telco.

Sample Callback (Successful)

{
  "ResponseCode": "0000",
  "Message": "success",
  "Data": {
    "Amount": 80,
    "Charges": 5,
    "AmountAfterCharges": 80,
    "Description": "The Telecel Cash payment has been approved and processed successfully",
    "ClientReference": "3jL2KlUy3vt21",
    "TransactionId": "09f84e20a283942e807128e8c21d08d6",
    "ExternalTransactionId": "2116938399",
    "AmountCharged": 85,
    "OrderId": "09f84e20a283942e807128e8c21d08d6",
    "PaymentDate": "2026-06-06T12:00:44Z"
  }
}

Sample Callback (Failed)

{
  "ResponseCode": "2001",
  "Message": "failed",
  "Data": {
    "Amount": 80,
    "Charges": 5,
    "AmountAfterCharges": 80,
    "Description": "FAILED: User entered wrong PIN",
    "ClientReference": "3jL2KlUy3vt21",
    "TransactionId": "09f84e20a283942e807128e8c21d08d6",
    "ExternalTransactionId": "2116938399",
    "AmountCharged": 85,
    "OrderId": "09f84e20a283942e807128e8c21d08d6",
    "PaymentDate": "2026-06-06T12:00:44Z"
  }
}

Transaction Status Check

Checks the status of a debit transaction. It is mandatory to implement this status check to verify the final transaction state if no webhook callback is received within 5 minutes of initiation.

To poll the status, send an HTTP GET request appending the unique client reference.

DetailDescription
API Endpointhttps://api.xtopay.co/v1/payments/:reference
Request TypeGET
Content Typeapplication/json
AuthenticationBasic Base64(clientId:clientSecret)

Request Parameters

ParameterTypeRequiredDescription
:referenceStringYesThe client reference of the transaction (3jL2KlUy3vt21).

Sample GET Request

curl https://api.xtopay.co/v1/payments/3jL2KlUy3vt21 \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET"

Sample Response (Paid)

200 OK

{
  "message": "Successful",
  "responseCode": "0000",
  "data": {
    "date": "2026-06-06T12:00:44Z",
    "status": "Paid",
    "transactionId": "09f84e20a283942e807128e8c21d08d6",
    "externalTransactionId": "2116938399",
    "paymentMethod": "mobilemoney",
    "clientReference": "3jL2KlUy3vt21",
    "currencyCode": "GHS",
    "amount": 80,
    "charges": 5,
    "amountAfterCharges": 75,
    "isFulfilled": true
  }
}

Sample Response (Unpaid / Pending)

200 OK

{
  "message": "Successful",
  "responseCode": "0000",
  "data": {
    "date": "2026-06-06T12:00:00Z",
    "status": "Unpaid",
    "transactionId": "09f84e20a283942e807128e8c21d08d6",
    "externalTransactionId": null,
    "paymentMethod": "mobilemoney",
    "clientReference": "3jL2KlUy3vt21",
    "currencyCode": "GHS",
    "amount": 80,
    "charges": 0,
    "amountAfterCharges": 80,
    "isFulfilled": null
  }
}

Response Codes & Actions

Response CodeDescriptionRequired Action
0000The transaction has been processed successfully.None. Fulfill the customer's purchase or services.
0001Request accepted. A webhook callback will be dispatched upon final resolution.Maintain the transaction status as pending. Await final callback.
2001debit failed (e.g. invalid PIN, user timeout, insufficient wallet funds, reached telco balance limits).Instruct the user to check wallet balances, resolve blockages, or try another wallet number.
4000Validation errors. Request format is incorrect.Verify parameter schemas (amounts, international phone prefixes) and retry.
4070declined by gateway. Gateway fees not set or transaction is below minimum thresholds.Check transaction limits or contact your relationship manager.
4101Permission issue: Public/Private keys mismatch, or direct receive scopes are disabled on this account keys.Ensure your keys are correctly set. Contact support to confirm that mobilemoney-receive-direct scopes are enabled.
4103Permission denied. Customer account is not allowed to transact on this channel.Instruct the customer to contact their network provider.

How is this guide?

Edit this page on GitHub
Last updated on June 6, 2026

On this page