API Documentation

Everything you need to integrate Klapa. Base URL: https://klapa.pfeffernusstechnologies.dev/api/v1

Authentication

All endpoints require your secret key in the Authorization header. The key decides the mode: sk_test_… hits the sandbox, sk_live_… moves real money. Amounts are decimal strings in major units ("150.00" = GHS 150).

Authorization: Bearer sk_test_your_secret_key
Content-Type: application/json

Create an account to replace the placeholder with your own test key.

1 · Accept a payment (redirect)

Initialize a transaction, redirect your customer to the returned authorization_url, and we handle mobile money prompts, card + OTP and everything in between. Afterwards, always verify server-side.

curl -X POST https://klapa.pfeffernusstechnologies.dev/api/v1/transactions/initialize/ \
  -H "Authorization: Bearer sk_test_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "customer@example.com",
    "amount": "150.00",
    "currency": "GHS",
    "callback_url": "https://yourapp.com/payment/complete",
    "metadata": {"order_id": "ORD-1234"}
  }'
import requests

resp = requests.post(
    "https://klapa.pfeffernusstechnologies.dev/api/v1/transactions/initialize/",
    headers={"Authorization": "Bearer sk_test_your_secret_key"},
    json={
        "email": "customer@example.com",
        "amount": "150.00",
        "currency": "GHS",
        "callback_url": "https://yourapp.com/payment/complete",
    },
)
data = resp.json()["data"]
# redirect the customer to data["authorization_url"]
const resp = await fetch("https://klapa.pfeffernusstechnologies.dev/api/v1/transactions/initialize/", {
  method: "POST",
  headers: {
    Authorization: "Bearer sk_test_your_secret_key",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "customer@example.com",
    amount: "150.00",
    currency: "GHS",
    callback_url: "https://yourapp.com/payment/complete",
  }),
});
const { data } = await resp.json();
// redirect the customer to data.authorization_url

Response

{
  "status": true,
  "message": "Authorization URL created",
  "data": {
    "authorization_url": "https://klapa.pfeffernusstechnologies.dev/pay/chk_…/",
    "access_code": "chk_…",
    "reference": "KLP_…"
  }
}

2 · Verify a transaction

Never trust the redirect alone — confirm the status from your server before delivering value.

curl https://klapa.pfeffernusstechnologies.dev/api/v1/transactions/verify/KLP_XXXXXXXX/ \
  -H "Authorization: Bearer sk_test_your_secret_key"

3 · Direct mobile money charge

Charge a wallet without redirecting — you collect the phone number in your own UI.

curl -X POST https://klapa.pfeffernusstechnologies.dev/api/v1/charges/ \
  -H "Authorization: Bearer sk_test_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "customer@example.com",
    "amount": "25.00",
    "mobile_money": {"phone": "0551234567", "provider": "mtn"}
  }'

Success

any phone, e.g. 0551234567

Declined

phone ending 0000

Timeout

phone ending 1111

Live mode: a real debit prompt is sent to the subscriber's handset, so the charge returns 202 with status processing. Poll /transactions/verify/{reference}/ or listen for the charge.success / charge.failed webhook for the outcome.

4 · Webhooks

We POST signed JSON events to your endpoint. Verify the X-Klapa-Signature header (HMAC-SHA512 of the raw body with your endpoint's whsec_… secret) before processing. Respond with 2xx within 10s; failures are retried up to 6 times with exponential backoff.

# Django example
import hashlib, hmac, json

def klapa_webhook(request):
    signature = request.headers.get("X-Klapa-Signature", "")
    expected = hmac.new(WEBHOOK_SECRET.encode(), request.body, hashlib.sha512).hexdigest()
    if not hmac.compare_digest(expected, signature):
        return HttpResponse(status=401)
    event = json.loads(request.body)
    if event["event"] == "charge.success":
        fulfil_order(event["data"]["reference"])
    return HttpResponse(status=200)

Event types

charge.success charge.failed refund.processed transfer.success transfer.failed settlement.completed

5 · Send money (transfers)

Create a recipient once, then transfer from your balance. Resolve account names first for safety.

# 1. (Optional) name enquiry
curl -X POST https://klapa.pfeffernusstechnologies.dev/api/v1/misc/resolve/ \
  -H "Authorization: Bearer sk_test_your_secret_key" -H "Content-Type: application/json" \
  -d '{"account_number": "0551234567", "provider": "mtn"}'

# 2. Create a recipient
curl -X POST https://klapa.pfeffernusstechnologies.dev/api/v1/recipients/ \
  -H "Authorization: Bearer sk_test_your_secret_key" -H "Content-Type: application/json" \
  -d '{"type": "mobile_money", "name": "Ama Mensah", "account_number": "0551234567", "provider": "mtn"}'

# 3. Send the transfer (uses the RCP_ code from step 2)
curl -X POST https://klapa.pfeffernusstechnologies.dev/api/v1/transfers/ \
  -H "Authorization: Bearer sk_test_your_secret_key" -H "Content-Type: application/json" \
  -d '{"recipient": "RCP_xxxxxxxx", "amount": "50.00", "reason": "Refund for order 1234"}'

Endpoint reference

MethodPathPurpose
POST/transactions/initialize/Create a hosted checkout session
GET/transactions/verify/{reference}/Confirm a transaction's status
GET/transactions/?status=&from=&to=List transactions (paginated)
POST/charges/Direct mobile money charge
POST/customers/Create / upsert a customer
GET/customers/{code}/Fetch a customer
POST/refunds/Refund a transaction (full/partial)
POST/recipients/Create a transfer recipient
POST/transfers/Send a payout
GET/balance/Available balance per currency
GET/misc/banks/?country=ghanaBank list with codes
GET/misc/providers/?country=ghanaMobile money providers
POST/misc/resolve/Account name enquiry

Test instruments

Card — success

4084084084084081

Any future expiry · any CVV · OTP 123456

Card — declined

4084080000005408

Card — insufficient funds

4084080000000409

Rate limit: 120 requests/minute per key. Errors return {"status": false, "message": "…"} with conventional HTTP codes (401, 404, 409, 422, 429).