Transactions

Create and inspect purchases. One endpoint covers airtime, data, voice, AFA, ECG, and future product types via the product field.

How to use

  1. Resolve catalog ids from GET /products (do not hard-code).
  2. POST /transactions with transactions:create + the product scope, a unique Idempotency-Key, and the fields for that product type.
  3. Track status with GET /transactions/{id}, GET /transactions/{id}/status, or webhooks.

Product-specific walks: Airtime · Data · ECG · Voice & AFA.

Create transaction

Scopes: transactions:create plus the product scope (e.g. airtime:purchase).

POSThttps://confident-elephant-309.convex.site/v1/transactions
Request
curl -sS https://confident-elephant-309.convex.site/v1/transactions \  -H "Authorization: Bearer $KEY" \  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \  -H "Content-Type: application/json" \  -d '{    "externalReference": "ORDER-123",    "product": "airtime",    "productId": "airtime-mtn",    "amount": 5,    "beneficiary": "0241234567"  }'
201 Created
{  "transaction": {    "id": "511260",    "reference": "511260",    "status": "pending",    "kind": "purchase",    "environment": "sandbox"  },  "purchase": {    "id": "511260",    "reference": "511260",    "status": "pending",    "kind": "purchase"  }}

Idempotency vs externalReference

FieldRole
Idempotency-KeyPrevents duplicate execution on retries; response is replayed
externalReferenceYour order / reconciliation id (unique per app + environment)

Request body

FieldRequiredDescription
idempotencyKeyYes*Or Idempotency-Key header (UUID recommended)
productYesdata | airtime | voice | afa | ecg | ecg_prepaid | ecg_postpaid
productIdUsuallyPartner code e.g. airtime-mtn (from GET /products)
amountWhen sold by amountMajor units (GHS)
beneficiaryAirtime / data / voiceMSISDN e.g. 024…
packageIdData / AFA packagesCatalog package id; charged at catalog developer price
meterNumberECGAfter meter lookup
meterIdECGFrom meters/lookup when required
categoryECGPREPAID | POSTPAID
phoneNumberECG optionalCustomer contact for utility
externalReferenceNoYour order id for reconciliation

Get transaction

Scope: transactions:read

GEThttps://confident-elephant-309.convex.site/v1/transactions/{id}
Request
curl -sS https://confident-elephant-309.convex.site/v1/transactions/511260 \  -H "Authorization: Bearer $KEY"
200 OK
{  "transaction": {    "id": "511260",    "reference": "511260",    "status": "completed",    "kind": "purchase",    "environment": "sandbox",    "amount": 1,    "currency": "GHS",    "beneficiary": "0241234567",    "externalReference": "ORDER-123",    "token": null,    "errorCode": null,    "customerMessage": null,    "createdAt": 1793722011260,    "completedAt": 1793722011394  }}

Status poll

GEThttps://confident-elephant-309.convex.site/v1/transactions/{id}/status
Request
curl -sS https://confident-elephant-309.convex.site/v1/transactions/511260/status \  -H "Authorization: Bearer $KEY"
200 OK
{  "id": "511260",  "status": "completed",  "reference": "511260"}

Statuses include pending, processing, completed, failed. Prefer webhooks for final state.

Legacy alias

POST /purchases and GET /purchases/{id} behave like the transactions routes. New integrations should use /transactions.

Service guides: Airtime, Data, ECG, Voice & AFA.