POST /api/v1/collect
Initiate a mobile money collection from a customer's wallet.
✅
Building a full deposit flow? Follow the Integration Guide.
Endpoint
output.txtText
POST https://api.rohopay.com/api/v1/collect
Headers
| Header | Required | Value |
|---|---|---|
Authorization | ✅ | Bearer {api_key} |
Content-Type | ✅ | application/json |
Idempotency-Key | ✅ | Stable, unique per business event — see Idempotency |
Request Body
response.jsonJSON
{
"phone": "256700123456",
"amount": 50000,
"currency": "UGX",
"description": "Order #1001",
"callback_url": "https://your-app.com/webhooks/rohopay",
"payment_method": "mobile_money"
}
| Field | Type | Required | Description |
|---|---|---|---|
phone | string | ✅ | 256XXXXXXXXX — country code, no 0, no + (see Phone Formats) |
amount | integer | ✅ | Whole currency units — 50000 is UGX 50,000. No minor-unit multiplier. Min UGX 500, max UGX 100,000,000 |
currency | string | ✅ | UGX, KES, TZS, or RWF (no default) |
description | string | — | Shown on your dashboard |
callback_url | string | — | Public http(s) URL that receives the webhook. Private / localhost addresses are refused |
payment_method | string | — | Defaults to "mobile_money" |
Response (200 OK)
The response means the request was accepted, not that the customer paid.
status is pending until the customer approves the prompt.
response.jsonJSON
{
"success": true,
"data": {
"transaction_id": "01j2k3m4n5p6q7r8s9t0uvwx",
"reference": "ref_ab12cd34ef56ab78",
"status": "pending",
"amount": 50000,
"currency": "UGX",
"commission": 500,
"provider": "rohopay",
"net_amount": 49500
},
"message": "Payment request initiated"
}
| Field | Description |
|---|---|
reference | Store this. It matches the webhook's reference and is what you pass to GET /api/v1/transactions/{reference} |
transaction_id | RohoPay's internal id (also echoed in webhooks) |
commission | RohoPay's commission on this collection |
net_amount | What your wallet is credited once the payment succeeds |
⚠️
Never credit a customer or fulfil an order from this response. Wait for the
webhook (or poll) to report status: "successful".
Error Responses
| HTTP | Code | Description |
|---|---|---|
| 400 | MISSING_IDEMPOTENCY_KEY | Idempotency-Key header missing (flat body: {"error", "message"}) |
| 400 | INVALID_REQUEST | Body is not valid JSON |
| 400 | INVALID_PHONE | Phone is not a supported international number |
| 400 | AMOUNT_TOO_LOW / AMOUNT_TOO_HIGH | Outside the allowed range |
| 400 | INVALID_CALLBACK_URL | callback_url must start with http:// or https:// |
| 401 | INVALID_API_KEY | Missing, unknown, or inactive API key |
| 403 | PROJECT_INACTIVE | The project behind this key is disabled |
| 429 | TEST_LIMIT_EXCEEDED | Test-mode daily limit reached (10/day) |
| 429 | RATE_LIMITED | Too many requests |
| 502 | PROVIDER_ERROR | The payment could not be initiated — retry as a new attempt |
Errors are returned in the standard envelope:
response.jsonJSON
{ "success": false, "error": { "code": "INVALID_PHONE", "message": "…" } }
