Additional resources

Webhooks

Receive real-time status change notifications without polling the API.

How it works

  1. Expose an endpoint on your server, capable of receiving HTTP POST requests.
  2. Configure your URLs callback URLs in your application settings from the dashboard (creation or modification).
  3. Receive the event — on each status change (deposit, withdrawal or refund), KPay sends a POST to the corresponding URL.
  4. Verify the signature then process the event (idempotency required).
  5. Respond 200 OK quickly to acknowledge receipt and avoid retries.

Callback URL configuration

When creating or modifying your application, you can define up to 4 callback URLs:

Available URLs

Generic URL (fallback)string

URL used by default if no specific URL is defined for a given transaction type.

Deposits callbackstring

URL dedicated to incoming payment notifications (payment.* events).

Withdrawals callbackstring

URL dedicated to withdrawal notifications (payout.* events).

Refunds callbackstring

URL dedicated to refund notifications (refund.* events).

URL resolution

For each notification, KPay first looks for the URL specific to the type (deposit, withdrawal or refund). If it is not defined, the generic URL (fallback) is used. If no URL is configured, the notification is not sent.

Event object

Webhook payload
{
  "event": "payment.completed",
  "paymentId": "pay_abc123",
  "reference": "KPAY-DEP-12345",
  "status": "COMPLETED",
  "amount": 5000,
  "phoneNumber": "237670000001",
  "externalId": "ORDER-12345",
  "metadata": { "orderId": "12345" },
  "completedAt": "2026-05-14T10:02:30.000Z",
  "failedAt": null,
  "failureReason": null,
  "timestamp": "2026-05-14T10:02:31.000Z"
}

Properties

eventstring

Event type (see next section).

paymentIdstring

Unique KPay transaction identifier.

referencestring

Internal KPay transaction reference.

statusstring

Final status.

COMPLETEDFAILEDCANCELLED
amountnumber

Transaction amount.

phoneNumberstring

Customer phone number.

externalIdstring

Your transaction identifier (if provided at initialization).

metadataobject

Metadata passed at initialization.

completedAtstring | null

Completion timestamp (if COMPLETED).

failedAtstring | null

Failure timestamp (if FAILED/CANCELLED).

failureReasonstring | null

Failure reason if applicable.

timestampstring

Webhook send timestamp (ISO 8601).

Event types

Events are grouped by transaction type. Each category is sent to the corresponding callback URL (or the generic URL as fallback).

Deposits (incoming payments)

Withdrawals (payouts)

Refunds

Request headers

X-KPAY-Signaturestring

HMAC-SHA256 (hex) calculated on the raw JSON body received.

X-KPAY-Eventstring

Event name (e.g. payment.completed, payout.failed).

User-Agentstring

KPAY-Webhook/1.0

Security — signature verification

Always verify before processing

Calculate the HMAC on the raw body received (not re-serialized) and compare in constant time. Only process the event after validation. This signature (raw body) is distinct from the gateway return signature (status|reference|externalId|ts).
Node.js
const crypto = require("crypto");

app.post("/webhooks/kpay",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.headers["x-kpay-signature"];
    const raw = req.body; // Buffer brut
    const expected = crypto
      .createHmac("sha256", process.env.KPAY_WEBHOOK_SECRET)
      .update(raw)
      .digest("hex");
    const valid = signature &&
      crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
    if (!valid) return res.status(400).send("Invalid signature");
    const event = JSON.parse(raw.toString());
    // traiter de façon idempotente...
    res.status(200).send("OK");
  });

Retries & best practices

  • Retry policy: 3 attempts with backoff (1 s, 2 s, 4 s), 3 s timeout per attempt. No retry on 4xx response; retry on 5xx/network.
  • Respond quickly: return 200 before any long processing; process asynchronously if needed.
  • Idempotency: the same event may arrive multiple times; deduplicate via paymentId / externalId.
  • HTTPS required, valid certificate.
  • Optional URLs: if you define no callback URL, KPay will not attempt to send webhooks. You can always check the status via the API GET /api/v1/payments/:id.

Source of authority

The webhook is the source of authority for the final status. Polling GET /api/v1/payments/:id is the fallback complement.

Related resources

Was this page helpful?