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
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
| Method | Path | Purpose |
|---|---|---|
| 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=ghana | Bank list with codes |
| GET | /misc/providers/?country=ghana | Mobile 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).