Skip to main content

Initiate Collection (Checkout)

The Initiate Collection endpoint allows you to request payment from a customer. Use this when you want to collect money from users.

info

Collections currently only support mobile money channels (M-Pesa, Airtel Money, T-Kash). Bank transfers are not supported for collections.

Endpoint​

POST /collections/checkouts/initiate

Authentication​

Required: Yes (Bearer Token)

Authorization: Bearer YOUR_ACCESS_TOKEN

Request​

Request Body​

{
"payfrom": "+254712345678",
"amount": "1000.00",
"ref_no": "ORDER-2024-001",
"callback_url": "https://yourdomain.com/webhook",
"category": "ecommerce",
"narration": "Payment for Order #123",
"channel": 63902,
"app_code": 12345
}

Field Descriptions​

FieldTypeRequiredDescription
payfromstringYesCustomer phone number (E.164 format)
amountstringYesPayment amount in KES (decimal with 2 places)
ref_nostringYesYour unique reference number (max 50 chars)
account_numberstringNoYour account number for tracking. If not provided, defaults to the platform product name.
callback_urlstringYesURL for transaction notifications
categorystringNoTransaction category: ecommerce, gaming, adult_content, forex, or other (default)
narrationstringNoPayment description/narration sent to payment gateway (max 200 chars, default: "Request Payment")
channelintegerNoPayment channel code (e.g., 63902 for M-Pesa). Not required when payfrom is a phone number - the channel is automatically inferred from the phone carrier (Safaricom → M-Pesa, Airtel → Airtel Money, Telkom → T-Kash). See List Channels for available channels. Note: Collections only support mobile money channels.
app_codeintegerYesApp code for multi-app scenarios

Response​

Success Response (200 OK)​

{
"payfrom": "+254712345678",
"amount": "1000.00",
"ref_no": "ORDER-2024-001",
"account_number": "1001",
"callback_url": "https://yourdomain.com/webhook",
"status": 0,
"category": "ecommerce",
"channel": 63902,
"channel_name": "M-Pesa",
"channel_type": "Mobile Money",
"created_at": "12/04/2024 14:30:45"
}

Response Fields​

FieldTypeDescription
payfromstringCustomer phone number
amountstringPayment amount (KES)
ref_nostringYour reference number
account_numberstringAccount number used
callback_urlstringWebhook notification URL
statusintegerTransaction status code (0=Pending, 1=Processed, 2=Failed, 3=Completed)
categorystringTransaction category
channelintegerPayment channel code
channel_namestringPayment channel name (e.g., "M-Pesa", "Airtel Money")
channel_typestringChannel type ("Mobile Money" or "Bank")
created_atstringTimestamp (DD/MM/YYYY HH:MM:SS)

Status Codes​

CodeStatusDescription
200SuccessCollection initiated successfully
400Bad RequestInvalid parameters
401UnauthorizedInvalid or expired token
403ForbiddenMerchant not approved or app inactive
404Not FoundApp not found
500Server ErrorInternal server error

Error Responses​

400 Bad Request - Invalid Phone Number​

{
"detail": "Invalid phone number"
}

400 Bad Request - Invalid Amount​

{
"detail": "Invalid amount"
}

400 Bad Request - Invalid Channel​

{
"detail": "Invalid channel code: 99999"
}

400 Bad Request - Invalid Channel Type​

{
"detail": "Collections only support mobile money channels. NCBA Bank is a Bank."
}

401 Unauthorized​

{
"detail": "Invalid or expired token"
}

403 Forbidden - Merchant Not Approved​

{
"detail": "Your merchant profile is not approved. Please ensure your profile has been verified before initiating checkouts."
}

403 Forbidden - App Inactive​

{
"detail": "App is not active"
}

403 Forbidden - Product Not Enabled​

{
"detail": "App does not have collections product enabled"
}

403 Forbidden - IP Not Allowed​

{
"detail": "Access denied: IP address not allowed"
}
info

This error occurs when your app has IP restrictions configured and the request is coming from an IP address that is not in the allowed list. To fix this:

  • Add your server's IP address to the allowed IPs list in your app settings
  • Or remove all IP restrictions if you want to allow requests from any IP address

404 Not Found​

{
"detail": "App not found"
}

Examples​

curl -X POST https://sandbox.api.jpay.africa/api/v1/collections/checkouts/initiate \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
-H "Content-Type: application/json" \
-d '{
"payfrom": "+254712345678",
"amount": "1000.00",
"ref_no": "ORDER-2024-001",
"callback_url": "https://yourdomain.com/webhook",
"category": "ecommerce",
"narration": "Payment for Order #123",
"channel": 63902
}'

Collection Categories​

Use appropriate categories for collection organization and compliance:

CategoryDescription
ecommerceE-commerce and online shopping transactions
gamingGaming and entertainment transactions
adult_contentAdult content related transactions
forexForex and trading transactions
otherOther transaction types (default)
info

Category Usage: Categories help with transaction organization, reporting, and compliance. Choose the category that best matches your business type.

Important Notes​

danger

Before initiating collections, ensure:

  1. ✅ Your merchant account is APPROVED (profile_status = approved)
  2. ✅ Your app has collections product enabled
  3. ✅ Your app is ACTIVE
  4. ✅ Phone number is in E.164 format (e.g., +254712345678)
  5. ✅ Amount has 2 decimal places (e.g., 1000.00)
  6. ✅ Your server's IP address is whitelisted in the app's allowed IPs (if IP restrictions are configured)

Best Practices​

Phone Number Validation​

import phonenumbers

def format_phone_number(phone):
parsed = phonenumbers.parse(phone, "KE")
if phonenumbers.is_valid_number(parsed):
return phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
return None

Error Handling​

def create_collection_with_retry(payfrom, amount, ref_no, max_retries=3):
for attempt in range(max_retries):
response = requests.post(url, headers=headers, json=payload)

if response.status_code == 200:
return response.json()
elif response.status_code == 401:
refresh_token() # Refresh and retry
continue
elif response.status_code == 429:
time.sleep(2 ** attempt) # Exponential backoff
continue
else:
break

return None

Webhook Notifications​

When a collection is processed, JPay will POST to your callback_url with a standardized payload:

{
"notification": {
"type": "collection",
"event": "transaction.completed"
},
"data": {
"result_code": 0,
"result_description": "Transaction processed successfully",
"amount": "1000.00",
"ref_no": "ORDER-2024-001",
"beneficiary": {
"account": "+254712345678",
"kyc": null
},
"transaction_date": "2024-04-12T14:35:00.000Z",
"external_ref": "SPEJ12345",
"trans_id": "QGX1234567"
}
}

Webhook Payload Structure​

Notification Object​

FieldTypeDescription
typestringTransaction type: collection or payout
eventstringEvent type: transaction.created, transaction.failed, or transaction.completed

Data Object​

FieldTypeDescription
result_codeintegerResult code: 0 for success, any other value indicates failure
result_descriptionstringDescription of the transaction result or failure reason
amountstringTransaction amount in KES
ref_nostringYour reference number provided during initiation
beneficiaryobjectBeneficiary information
beneficiary.accountstringCustomer phone number (payfrom field) in E.164 format
beneficiary.kycstring or nullFull names of the payer. Currently null for collections
transaction_datestringTransaction timestamp in ISO 8601 format (UTC)
external_refstringExternal transaction reference from the payment gateway (may be empty)
trans_idstringPayment gateway transaction code (may be empty)

Event Types​

  • transaction.created - Transaction has been initiated
  • transaction.completed - Transaction completed successfully (result_code = 0)
  • transaction.failed - Transaction failed (result_code ≠ 0)
info

Result Code: A result_code of 0 indicates successful completion. Any other value indicates a failure, with details provided in result_description.

info

Transaction Date: The transaction_date field uses the completion time from the payment gateway when available, otherwise falls back to the creation time.

Next Steps​