Developers
Four steps to your first payment.
Initialise, redirect, receive a signed webhook, verify. This guide follows the public KonetPay documentation, which remains the authoritative reference; keys shown are placeholders.
- Base URL
- https://core-api.konetpay.com/api/
- Format
- JSON
- Currency
- NGN, amounts in kobo
- Mode
- Decided by the key
Keys and modes
Test and live never share data.
Every business has two isolated worlds. The key you send decides which one a request touches; you never send a mode flag. A key containing _test_ is a test request, and one containing _live_ moves real money.
- Secret key (
sk_…): server side only. Full capability. - Public key (
pk_…): checkout pages and status polling. No money movement. - Keys and webhook endpoints are managed in your KonetPay dashboard, not through the API.
- Test keys are issued immediately; live keys follow a compliance review.
Authorization: Bearer sk_test_XXXXXXXXXXXXXXXX
// A missing or invalid key returns 401.
// A public key on a secret-key endpoint returns 403.Step 1 and 2
Initialise, then redirect.
Create the payment on your server. You get back a reference, an access code and an authorization_url. Redirect the customer’s browser to it.
amount: integer in kobo. Minimum 5000 (₦50).reference: your unique value, up to 30 characters. Strongly recommended.fee_bearer:merchant(default) orcustomer.channels: onlybank_transferis live.- Initialising is limited to 60 requests per minute.
POST /api/transactions/initialize/
Authorization: Bearer sk_test_XXXX
{
"amount": 500000,
"email": "customer@example.com",
"currency": "NGN",
"reference": "your-unique-ref-001",
"callback_url": "https://yourapp.com/payment/return",
"fee_bearer": "merchant",
"metadata": { "order_id": "1234" }
}{
"status": "success",
"data": {
"reference": "KP…",
"access_code": "…",
"authorization_url": "https://checkout.konetpay.com/<access_code>"
}
}Step 3
Receive and verify the webhook.
Every webhook carries an X-Konetpay-Signature header: an HMAC-SHA512 of the raw request body, keyed with your secret key for that mode, hex encoded. Compute it over the bytes exactly as received, compare in constant time, and reject on mismatch.
- Events:
payment.initiated,payment.successful,payment.failed. - Success means any 2xx. Otherwise KonetPay retries up to two more times, five minutes apart.
- Delivery is at least once. Deduplicate on
data.reference. - Register up to two HTTPS endpoints, without a query string, in your dashboard.
- Reply fast, then process in the background.
const crypto = require("crypto");
app.use(express.json({
verify: (req, _res, buf) => { req.rawBody = buf; }
}));
app.post("/webhooks/konetpay", (req, res) => {
const expected = crypto
.createHmac("sha512", process.env.KONETPAY_SECRET_KEY)
.update(req.rawBody).digest("hex");
const received = req.headers["x-konetpay-signature"] || "";
const valid = expected.length === received.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
if (!valid) return res.sendStatus(401);
res.sendStatus(200);
// enqueue req.body, dedupe on req.body.data.reference
});Step 4
Verify before you fulfil.
As a second check, or if you prefer not to rely on the webhook alone, read the transaction straight from KonetPay. Give value only when the status is successful and the amount and currency match your order. Never credit an order because the browser landed on your callback address.
For a checkout page that must not hold a secret key, the lighter status endpoint accepts a public key.
GET /api/transactions/verify/<reference>/
Authorization: Bearer sk_test_XXXX{
"event_type": "payment.successful",
"data": {
"reference": "KP…",
"business_reference": "your-unique-ref-001",
"status": "successful",
"amount_minor": 500000,
"fee_minor": 5000,
"net_amount_minor": 495000,
"currency": "NGN",
"channel": "bank_transfer"
}
}Reference
The documented endpoints.
| Purpose | Method | Path | Auth |
|---|---|---|---|
| Initialise a transaction | POST | /api/transactions/initialize/ | Secret key |
| Verify by reference | GET | /api/transactions/verify/<reference>/ | Secret key |
| Check status (lightweight) | GET | /api/transactions/status/<reference>/ | Public key |
| Verify by access code (used by hosted checkout) | GET | /api/transactions/verify-access-code/<access_code>/ | No key |
| Issue a static virtual account | POST | /api/external/virtual-accounts/ | Secret key |
| List banks for virtual accounts | GET | /api/external/virtual-accounts/processors/ | Secret key |
| List or retrieve virtual accounts | GET | /api/external/virtual-accounts/[<id>/] | Secret key |
| Block a virtual account | POST | /api/external/virtual-accounts/<id>/block/ | Secret key |
Errors
One envelope: status, message, code and, for validation, errors.
| HTTP | Code | Meaning |
|---|---|---|
| 400 | bad_request | Malformed request. |
| 401 | unauthorized | Missing, invalid or revoked API key. |
| 403 | forbidden | Wrong key capability, for example a public key where a secret key is required. |
| 404 | not_found | The resource, such as a transaction reference, does not exist. |
| 422 | validation_error | Validation failed. Field-level details are in errors. |
| 429 | rate_limited | Too many requests. A Retry-After header is included. |
Limits and idempotency
- Initialise: 60 requests per minute. Other authenticated requests: 120. Unauthenticated: 60.
- Always send your own unique
reference. It ties the payment to your order across the webhook and the verify call. - Because webhooks are delivered at least once, make fulfilment idempotent: a repeat for a reference you already fulfilled is a no-op.
- Only NGN is supported, and only
bank_transferis live.
Before you go live
The go-live checklist.
Work through this list before you swap test keys for live keys.
- The integration works end to end with test keys.
- Your webhook endpoint is registered, uses HTTPS and returns 200 quickly.
- You verify the signature header on every webhook.
- Fulfilment is idempotent, deduplicated on the reference.
- You confirm status, amount and currency before giving value.
- Your business is approved and you have swapped to live keys.
- Secret keys live only on your server, never client side or in version control.
Need something this guide does not cover?
The official documentation is the authoritative reference. Include your business name and the transaction reference when you contact support.