Skip to main content
Payfonte sends webhook events when disbursement status changes (for example processing, success, failed). Use webhooks as your primary asynchronous disbursement update channel.

Payload Fields

FieldDescription
eventEvent type (for example disbursement.status)
clientIdYour Payfonte client ID
data.statusdisbursement status (processing, success, failed)
data.statusDescriptionProvider/platform status detail
data.referencePayfonte disbursement reference
data.externalReferenceMerchant-provided reference
data.providersReferenceProvider reference
data.amountdisbursement amount in minor units
data.chargeApplied disbursement charge
data.providerProvider slug used
data.transferRecipientIdRecipient identifier
data.transferRecipientLabelHuman-readable recipient label
deliveryIdWebhook delivery identifier
Sample payload:
disbursement.status
{
  "event": "disbursement.status",
  "clientId": "payfusion",
  "data": {
    "clientId": "payfusion",
    "type": "disbursement",
    "status": "success",
    "statusDescription": "Disbursement was successful",
    "reference": "L20250614142024AAAAA",
    "providersReference": "reference-from-mno",
    "externalReference": "merchant-reference",
    "currency": "XOF",
    "country": "BJ",
    "transferRecipientId": "684d561743dac722bcd43e9f",
    "transferRecipientLabel": "MTN MoMo | 2290123456789",
    "charge": 180,
    "amount": 10000,
    "provider": "mtn-momo-benin",
    "providerLabel": "MTN MoMo",
    "providerLogo": "https://payfonte.s3.amazonaws.com/mtn-momo.png",
    "channel": "mobile-money",
    "timestamp": "2025-06-14T14:20:25.023Z",
    "narration": "disbursement narration here",
    "completedAt": 1749910827
  },
  "deliveryId": "684d852b27e08e60f4d09103"
}

Signature Verification

Validate webhook authenticity before processing.
  • Header: x-webhook-signature
  • Algorithm: sha512 HMAC of request body using your client-secret
const crypto = require("crypto");

app.post("/payfonte/disbursement-webhook", express.json({ type: "*/*" }), (req, res) => {
  const secret = process.env.PAYFONTE_CLIENT_SECRET;
  const payload = JSON.stringify(req.body);
  const expected = crypto.createHmac("sha512", secret).update(payload).digest("hex");
  const received = req.headers["x-webhook-signature"];

  if (expected !== received) {
    return res.status(401).send("Invalid signature");
  }

  res.status(200).send("OK");
  processDisbursementWebhook(req.body);
});

Safe Processing Pattern

1

Acknowledge quickly

Return HTTP 200 immediately after basic validation.
2

Check idempotency

Deduplicate by reference + status (or deliveryId) before applying business actions.
3

Update internal ledger/state

Mark disbursement as final only for terminal statuses and save statusDescription.
4

Verify for critical disbursements

Optionally confirm final state using GET /billing/v1/disbursements/verify/{reference}.

Common Issues

IssueLikely CauseFix
Duplicate webhook processingNo idempotency handlingStore processed reference/status and skip repeats
Signature mismatchWrong secret or mutated payloadUse correct client-secret and hash exact request body
Missed status updatesTimeout/non-200 responsesReturn 200 quickly; move heavy logic to async worker
Conflicting disbursement stateOut-of-order processingAlways compare current state before applying updates

Disbursements Overview

End-to-end disbursement flow and endpoints.

Disbursement Examples

Request/response payload samples.

Authorization Mode

PIN vs Authorization URL configuration.