Skip to main content

Webhook Payload Examples

This guide provides complete webhook payload examples for each supported currency. Mavapay sends webhooks for two main event types: payment.received (DEPOSIT) and payment.sent (WITHDRAWAL).

Event Types

EventTypeDescription
payment.receivedDEPOSITTriggered when Mavapay receives funds from a customer
payment.sentWITHDRAWALTriggered when Mavapay sends funds to a beneficiary

Bitcoin (BTC) Webhooks

payment.received (Deposit)

Triggered when Bitcoin payment is received via Lightning Network.
{
  "event": "payment.received",
  "data": {
    "id": "9830a229-1db0-4a19-9e51-37cca51586f2",
    "walletId": "98b5093f-6913-48c6-acb4-8fdc46c7feec",
    "ref": "678ae2e20de0dee7aecd2bd40f8c9",
    "hash": "6834d0d46ce47f0012cb36ca",
    "amount": 3427,
    "fees": 17,
    "currency": "BTC",
    "type": "DEPOSIT",
    "status": "SUCCESS",
    "autopayout": true,
    "createdAt": "2025-05-26T20:37:23.566Z",
    "updatedAt": "2025-05-26T20:37:23.566Z",
    "btcUsdMetadata": {
      "id": "698daefd-1dd8-4420-9c0a-5d78080d7c27",
      "orderId": "58772-8830",
      "paymentMethod": "BANKTRANSFER",
      "externalRef": null,
      "customerInternalFee": 0,
      "estimatedRoutingFee": 0,
      "onChainAddress": null,
      "lnInvoice": "lntbs34270n1p5rf5rlpp56xqq6cj9ml75v0yl6fsfrlmwfmaygnal8gd706l3epzethymhclsdz5296...",
      "lnAddress": null,
      "createdAt": "2025-05-26T20:37:23.576Z",
      "updatedAt": "2025-05-26T20:37:23.576Z"
    }
  }
}

Key Fields

FieldDescription
amountAmount in satoshis
feesTransaction fees in satoshis
hashPayment hash for tracking
btcUsdMetadata.lnInvoiceThe Lightning invoice that was paid
btcUsdMetadata.orderIdAssociated order ID

Nigerian Naira (NGN) Webhooks

payment.received (Deposit)

Triggered when NGN payment is received in your Mavapay account.
{
  "event": "payment.received",
  "data": {
    "id": "0b234e32-d5ba-4c2a-8f71-0b820a477f01",
    "walletId": "b7e7b823-b572-41d3-86a6-a6872f35c274",
    "ref": "f05432ecab06ca0357596af0366de",
    "hash": "001f7fb879bb3874eb057dcce06437d45d4020d7f348a36cd583b658e98e07d6",
    "amount": 300000,
    "fees": 2500,
    "currency": "NGN",
    "type": "DEPOSIT",
    "status": "SUCCESS",
    "autopayout": true,
    "createdAt": "2025-01-17T13:20:20.575Z",
    "updatedAt": "2025-01-17T13:20:20.575Z",
    "transactionMetadata": {
      "id": "570cba51-d9fd-402a-8b10-5c43e23077d8",
      "orderId": "82958-2591",
      "bankCode": "09026",
      "bankName": "KUDA MICROFINANCE BANK",
      "bankAccountName": "OLADEJI OLAOLU",
      "bankAccountNumber": "0123456789",
      "customerInternalFee": 0,
      "createdAt": "2025-02-03T14:18:20.737Z",
      "updatedAt": "2025-02-03T14:18:22.777Z"
    }
  }
}

Key Fields

FieldDescription
amountAmount in kobo (1 NGN = 100 kobo)
feesTransaction fees in kobo
transactionMetadata.bankAccountNameSender’s bank account name
transactionMetadata.bankAccountNumberSender’s account number
transactionMetadata.bankNameSender’s bank name
NGN amounts are in kobo. Divide by 100 to get the amount in Naira.

South African Rand (ZAR) Webhooks

payment.sent (Withdrawal)

Triggered when ZAR payment is sent to a beneficiary.
{
  "event": "payment.sent",
  "data": {
      "id": "14cfbe34-91b3-45ef-b1b0-bc7d55df7f0f",
      "walletId": "99a8272d-c8d7-436b-952b-a13ef816df9c",
      "ref": "d386f2c1260d53ad60bead81e1c99",
      "hash": "08f7198fd7dd73c087b4754a20d6f63f5b87bb8fcdc10e4d5cacd27650fce2ba",
      "amount": 5000,
      "fees": 0,
      "currency": "ZAR",
      "type": "WITHDRAWAL",
      "status": "PENDING",
      "autopayout": true,
      "createdAt": "2025-01-30T15:23:06.097Z",
      "updatedAt": "2025-01-30T15:23:09.768Z",
      "zarTransactionMetadata": {
          "id": "7801c544-2f05-4914-91e1-d157848ac569",
          "merchantId": "f77986d6-75df-4979-85a9-8acedcfca392",
          "orderId": "47581-9962",
          "merchantName": "Ricki Allardice",
          "bankName": "CAPITEC BANK",
          "bankAccountNumber": "1352906218",
          "reference": "MPYMX76EvJD2FYmUAkNd",
          "customerInternalFee": 0,
          "createdAt": "2025-01-30T15:23:06.099Z",
          "updatedAt": "2025-01-30T15:23:09.772Z"
      }
  }
}

Key Fields

FieldDescription
amountAmount in cents (1 ZAR = 100 cents)
statusCan be PENDING, SUCCESS, or FAILED
zarTransactionMetadata.merchantNameBeneficiary name
zarTransactionMetadata.bankAccountNumberBeneficiary account number
zarTransactionMetadata.referenceUnique transaction reference
ZAR amounts are in cents. Divide by 100 to get the amount in Rand.

Kenyan Shilling (KES) Webhooks

payment.sent (Withdrawal)

Triggered when KES payment is sent via M-Pesa.
{
  "event": "payment.sent",
  "data": {
      "id": "14cfbe34-91b3-45ef-b1b0-bc7d55df7f0f",
      "walletId": "99a8272d-c8d7-436b-952b-a13ef816df9c",
      "ref": "d386f2c1260d53ad60bead81e1c99",
      "hash": "08f7198fd7dd73c087b4754a20d6f63f5b87bb8fcdc10e4d5cacd27650fce2ba",
      "amount": 5000,
      "fees": 0,
      "currency": "KES",
      "type": "WITHDRAWAL",
      "status": "SUCCESS",
      "autopayout": true,
      "createdAt": "2025-01-30T15:23:06.097Z",
      "updatedAt": "2025-01-30T15:23:09.768Z",
      "kesTransactionMetadata": {
          "id": "7801c544-2f05-4914-91e1-d157848ac569",
          "transactionId": "14cfbe34-91b3-45ef-b1b0-bc7d55df7f0f",
          "paymentId": "f77986d6-75df-4979-85a9-8acedcfca392",
          "customerInternalFee": 0,
          "identifierType": "paytophone",
          "identifiers": {
            "phoneNumber": "+254796980711"
          },
          "createdAt": "2025-01-30T15:23:06.099Z",
          "updatedAt": "2025-01-30T15:23:09.772Z"
      }
  }
}

Key Fields

FieldDescription
amountAmount in cents (1 KES = 100 cents)
kesTransactionMetadata.identifierTypeM-Pesa payment type (paytophone, paytobill, paytotill)
kesTransactionMetadata.identifiersM-Pesa payment details (varies by type)
KES amounts are in cents. Divide by 100 to get the amount in Shillings.

Transaction Status Values

StatusDescription
PENDINGTransaction is being processed
SUCCESSTransaction completed successfully
FAILEDTransaction failed

Handling Webhooks

1. Verify Webhook Signature

Always verify the webhook signature to ensure it’s from Mavapay:
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

2. Process Webhook Events

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-mavapay-signature'];
  const isValid = verifyWebhookSignature(
    req.body,
    signature,
    process.env.WEBHOOK_SECRET
  );

  if (!isValid) {
    return res.status(400).json({ error: 'Invalid signature' });
  }

  const { event, data } = req.body;

  switch (event) {
    case 'payment.received':
      // Handle deposit
      handleDeposit(data);
      break;
    case 'payment.sent':
      // Handle withdrawal
      handleWithdrawal(data);
      break;
    default:
      console.log(`Unhandled event type: ${event}`);
  }

  res.json({ received: true });
});

3. Implement Idempotency

Use the transaction ID to prevent duplicate processing:
function handleDeposit(data) {
  const { id, amount, currency, status } = data;
  
  // Check if already processed
  if (alreadyProcessed(id)) {
    console.log(`Transaction ${id} already processed`);
    return;
  }
  
  // Process the deposit
  processDeposit({ id, amount, currency, status });
  
  // Mark as processed
  markAsProcessed(id);
}

Best Practices

  1. Respond Quickly: Return a 200 status within 5 seconds to acknowledge receipt
  2. Process Asynchronously: Handle heavy processing in background jobs
  3. Store Raw Payloads: Log raw webhook payloads for debugging
  4. Handle Retries: Implement idempotency to handle duplicate webhooks
  5. Monitor Failures: Set up alerts for failed webhook deliveries

Testing Webhooks

Use our staging environment to test webhook integration:
https://staging.api.mavapay.co/api/v1
Consider using tools like:

Next Steps