Initialize a payment

Initiate a mobile-money collection. JKAPay validates your request, creates a transaction record, and sends a payment prompt to the customer's phone. The customer authorises (or declines) the payment, then JKAPay POSTs the final state to the callbackUrl you supplied.

POST/v1/payments/initialize

Request body

Parameters
amount
string | integerrequired
The amount to charge. Pass a GHS string like "15.00" or an integer in pesewas (1500). Only two decimal places are honoured.
currency
string
Defaults to GHS. Currently the only supported value.
customer.msisdn
stringrequired
The customer's mobile-money phone number in any Ghanaian format: 0244000000, +233244000000, or 233244000000. JKAPay normalises before dispatch.
customer.network
stringrequired
The customer's network. One of MTN, VODAFONE (alias TELECEL), or AIRTELTIGO (alias AT).
customer.email
string
Optional. Used by some networks for receipt delivery. Maximum 160 characters.
description
stringrequired
A brief description of what the customer is paying for. Displayed on their phone prompt. Maximum 255 characters.
callbackUrl
stringrequired
The URL JKAPay POSTs the final transaction state to. Must be HTTPS in production. See Webhooks for the payload shape and signature scheme.
merchantRef
string
Your own reference for this transaction (order ID, invoice number, etc.). Echoed back to your callback as clientReference. Maximum 80 characters. Strongly recommended for reconciliation.
idempotencyKey
string
Up to 80 characters. Subsequent calls with the same key return the original transaction instead of creating a duplicate. Strongly recommended on every initialize call.
metadata
object
Arbitrary key-value pairs (strings, numbers, booleans). Echoed back unchanged on every callback and retrieve. Useful for app-specific context.
callbackUrl fallback. If you've configured a webhook URL on your Settings page, JKAPay will fall back to it when callbackUrl is omitted. We strongly recommend passing callbackUrl per-request anyway — it keeps the integration explicit and lets you point staging traffic at a different URL without changing your dashboard.

Sample request

cURL
curl -X POST https://api.jkapay.com/v1/payments/initialize \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "X-JKAPay-Merchant-Id: MCH_XXXXXXXXXXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "1.00",
    "customer": {
      "msisdn": "0244000000",
      "network": "MTN"
    },
    "description": "Order #12345",
    "callbackUrl": "https://example.com",
    "merchantRef": "ORD-12345",
    "idempotencyKey": "ORD-12345",
    "metadata": { "orderId": "12345", "channel": "web" }
  }'

Response fields

Response (transaction)
reference
string
JKAPay's unique identifier for the transaction (e.g. JKA_M3A8_8F2E1B4C). Use this to retrieve or correlate.
clientReference
string | null
The merchantRef you sent in the request, echoed back. Useful for reconciling against your own system.
status
string
One of PENDING, PROCESSING, SUCCESS, FAILED. Initialize returns PROCESSING once the customer has been prompted.
type
string
The transaction type. For payment initiation this is always COLLECTION.
amount
string
Transaction amount as a GHS string (e.g. "1.00").
fee
string
Platform fee. Zero at initialization, computed when the transaction succeeds.
net
string
Amount after platform fee. Zero at initialization, computed when the transaction succeeds.
currency
string
The currency code. Always GHS today.
customer
object
The customer object with msisdn (normalised to international format), name (resolved post-success when available), email (if supplied), and network.
description
string
The description you supplied in the request.
failureReason
string | null
Set only when status becomes FAILED. A short, merchant-safe reason.
metadata
object | null
Whatever metadata you sent on the request, echoed back unchanged.
initiatedAt
string (ISO 8601)
When JKAPay accepted the request.
completedAt
string (ISO 8601) | null
When the transaction reached a final state (SUCCESS or FAILED). Null while pending.
Response201
JSON
{
  "transaction": {
    "reference": "JKA_M3A8_8F2E1B4C",
    "clientReference": "ORD-12345",
    "status": "PROCESSING",
    "type": "COLLECTION",
    "amount": "1.00",
    "fee": "0.00",
    "net": "0.00",
    "currency": "GHS",
    "customer": {
      "msisdn": "233244000000",
      "name": null,
      "email": null,
      "network": "MTN"
    },
    "description": "Order #12345",
    "failureReason": null,
    "metadata": { "orderId": "12345", "channel": "web" },
    "initiatedAt": "2026-05-25T16:30:00.000Z",
    "completedAt": null
  }
}
Status flow. A new transaction starts as PENDING (created in our DB), moves to PROCESSING once the customer prompt has been dispatched, then settles to either SUCCESS or FAILED. The final state is delivered to your callbackUrl — your backend should never trust the initialize response as final.
Calling with a test key. Requests authenticated with sk_test_… are routed through JKAPay's simulator instead of a real mobile-money network — no real prompt is sent and no money moves, but the request, response, and webhook shapes are identical to live. Specific phone-number endings trigger specific outcomes (success, failure, slow success, etc.). See Sandbox / test mode for the full table.

Errors

  • 400 VALIDATION_ERROR — the request body failed validation. Inspect error.details.
  • 401 UNAUTHENTICATED — missing, malformed, or revoked API key.
  • 403 FORBIDDEN — your merchant account is suspended or not allowed to accept payments.
  • 409 CONFLICT — an in-flight transaction with the same idempotencyKey is still pending.
  • 502 PROVIDER_ERROR — the mobile network was unreachable or returned an error. Safe to retry with the same idempotencyKey.