API Reference
Collections API
Pull a payment from a customer's phone. Four methods, one request shape: push a prompt to their handset (USSD Push, STK Push, or Selcom Pesa Push), or hand them a Dynamic QR code to scan — no phone number needed.
Endpoints
/v1/collections/ussd-pushPush a USSD prompt to the customer's phone.
Idempotency-Key required/v1/collections/stk-pushPush an STK (SIM Toolkit) prompt.
Idempotency-Key required/v1/collections/selcom-pesa-pushPush a Selcom Pesa wallet prompt.
Idempotency-Key required/v1/collections/dynamic-qrGenerate a scannable QR code — no phone number required.
Idempotency-Key required/v1/merchants/{merchant_id}/collectionsList collections for a merchant.
dashboard/v1/merchants/{merchant_id}/collections/{collection_id}Get a single collection.
dashboardPush a collection
The three push endpoints share one request body — customer_phone is required for all three (validated: digits, optional leading +, 9–15 digits). Which endpoint you call is the method — there's no separate method field to set.
{
"merchant_id": "5c1f0b2a-3e21-4b9a-9c33-2f6a1d0e8b71",
"amount": "25000.00",
"currency": "TZS",
"customer_phone": "+255712345678",
"merchant_reference": "ORDER-4821",
"description": "2 bags of maize flour"
}{
"success": true,
"data": {
"id": "9b7e2c1a-...",
"merchant_id": "5c1f0b2a-3e21-4b9a-9c33-2f6a1d0e8b71",
"method": "STK_PUSH",
"amount": "25000.00",
"currency": "TZS",
"customer_phone": "+255712345678",
"merchant_reference": "ORDER-4821",
"payment_link_id": null,
"invoice_id": null,
"status": "processing",
"provider": "mock_selcom",
"provider_reference": "MOCK-SELCOM-9F3A1C2B",
"transaction_reference": "TXN-...",
"message": "An STK push was sent to the customer's phone — awaiting approval.",
"expires_at": null,
"initiated_at": "2026-08-14T09:00:00Z",
"completed_at": null,
"created_at": "2026-08-14T09:00:00Z",
"updated_at": "2026-08-14T09:00:00Z"
}
}Generate a Dynamic QR code
/v1/collections/dynamic-qr omits customer_phone entirely and returns two extra fields your app renders as a scannable code:
{
"success": true,
"data": {
"id": "2d4f8a91-...",
"method": "DYNAMIC_QR",
"amount": "15000.00",
"currency": "TZS",
"status": "processing",
"qr_payload": "selcompay://qr?ref=MOCK-SELCOM-7C1E&amount=15000¤cy=TZS",
"qr_expires_at": "2026-08-14T09:05:00Z",
"qr_image_url": null,
"expires_at": "2026-08-14T09:05:00Z",
"message": "Scan the QR code with a mobile money app to complete payment.",
"...": "same fields as a push collection"
}
}qr_image_url is always null today — render qr_payload as a QR code client-side (e.g. with any standard QR-rendering library).
Request fields
| Field | Type | Notes |
|---|---|---|
| merchant_id | uuid | Your merchant account ID. |
| amount | decimal string | Must be greater than 0. |
| currency | string | Defaults to "TZS". |
| customer_phone | string | Required on all push endpoints; omitted on dynamic-qr. |
| customer_id | uuid, optional | Link this collection to a saved customer. |
| customer_name | string, optional | Shown in the merchant dashboard alongside the collection. |
| customer_email | string, optional | Shown in the merchant dashboard alongside the collection. |
| merchant_reference | string, optional | Your own order/reference — max 100 characters. |
| payment_link_id | uuid, optional | Cross-validated: must belong to this merchant, be ACTIVE, and accept this method. |
| invoice_id | uuid, optional | Links this collection to an invoice — marked paid once the collection resolves successfully. |
| description | string, optional | Shown in the merchant dashboard. |
| callback_url | string, optional | Recorded alongside the collection; webhooks remain the delivery mechanism (see below). |
Resolving a collection
A push or QR collection always comes back status: "processing" — the customer still has to approve it on their phone. It resolves to successful or failed asynchronously — listen for the collection.success / collection.failed webhook, or poll GET .../collections/{collection_id}.
Webhooks are the reliable way to resolve a collection
collection.success/collection.failed on the Webhooks page instead.