Webhooks Guide

Set up webhooks to receive real-time payment notifications

API Base URL

All webhook API requests should be made to the following base URL:

https://wallet.wearemarz.com/api/v1

Overview

Callbacks are HTTP POST requests sent to your application when payment events occur, such as payment completion or failure. This allows for real-time updates without polling the API.

How to Receive Callbacks

To receive callbacks, simply include a callback_url parameter in any API request that supports callbacks (such as collection requests).

Example Request

POST /api/v1/collections
{
  "amount": 10000,
  "phone_number": "+256712345678",
  "callback_url": "https://your-domain.com/webhook/callback"
}

Important Notes

  • Include callback_url in your request to receive callbacks
  • Your callback endpoint must accept POST requests
  • Your callback endpoint should return HTTP 200 to acknowledge receipt
  • Callbacks are sent asynchronously after transaction processing is complete

Callback Flow

Understanding how callbacks work in our system and when you'll receive notifications.

Complete transaction flow (mobile money)

  1. You create a collection request with a callback_url parameter
  2. MTN/Airtel processes the payment
  3. MTN/Airtel sends callback to our system
  4. Our system processes the callback
  5. Transaction status is updated (completed, failed, or cancelled)
  6. Business balance is updated (if successful)
  7. Callback is sent to your provided callback_url (HTTP POST, same payload structure below)
  8. All processing complete

Card payments: Same flow—customer completes payment on the card gateway, our system receives the result, updates the transaction, then sends the same style of callback (POST to your callback_url) with provider set to "card payments".

When Callbacks Are Sent

Callbacks are sent for ALL final transaction statuses:

  • Completed: Payment successful, balance updated
  • Failed: Payment failed, no balance change
  • Cancelled: Payment cancelled, no balance change
  • All internal processing complete

Callbacks are NOT sent for: pending, processing, or any intermediate statuses.

Callback Payload Structure

The structure of the data sent to your callback URL. The event_type and transaction status will vary based on the transaction outcome.

Collection Object Fields

  • provider_transaction_id: A unified field containing the transaction ID from the payment provider. This single field works for both MTN and Airtel - it automatically contains the correct provider transaction ID based on which provider processed the payment. The field is included in the callback when the provider transaction ID is available.
  • provider: The payment provider used (mtn or airtel)
  • phone_number: The phone number used for the transaction
  • amount: The transaction amount with formatted and raw values
  • mode: The transaction mode (e.g., mtnuganda, airteluganda, sandbox)

Successful Payment Payload (MTN Example)

{
  "event_type": "collection.completed",
  "transaction": {
    "uuid": "transaction-uuid",
    "reference": "transaction-reference",
    "status": "completed",
    "amount": {
      "formatted": "10,000.00",
      "raw": 10000,
      "currency": "UGX"
    },
    "provider": "mtn",
    "phone_number": "+256712345678",
    "description": "Payment description",
    "created_at": "2025-08-20T15:18:48.000000Z",
    "updated_at": "2025-08-20T15:18:48.000000Z"
  },
  "collection": {
    "provider": "mtn",
    "phone_number": "+256712345678",
    "amount": {
      "formatted": "10,000.00",
      "raw": 10000,
      "currency": "UGX"
    },
    "mode": "mtnuganda",
    "provider_transaction_id": "MTN_FINANCIAL_TRANSACTION_ID"
  }
}

Successful Payment Payload (Airtel Example)

{
  "event_type": "collection.completed",
  "transaction": {
    "uuid": "transaction-uuid",
    "reference": "transaction-reference",
    "status": "completed",
    "amount": {
      "formatted": "10,000.00",
      "raw": 10000,
      "currency": "UGX"
    },
    "provider": "airtel",
    "phone_number": "+256712345678",
    "description": "Payment description",
    "created_at": "2025-08-20T15:18:48.000000Z",
    "updated_at": "2025-08-20T15:18:48.000000Z"
  },
  "collection": {
    "provider": "airtel",
    "phone_number": "+256712345678",
    "amount": {
      "formatted": "10,000.00",
      "raw": 10000,
      "currency": "UGX"
    },
    "mode": "airteluganda",
    "provider_transaction_id": "AIRTEL_MONEY_ID"
  }
}

Failed Payment Payload

{
  "event_type": "collection.failed",
  "transaction": {
    "uuid": "transaction-uuid",
    "reference": "transaction-reference",
    "status": "failed",
    "amount": {
      "formatted": "10,000.00",
      "raw": 10000,
      "currency": "UGX"
    },
    "provider": "mtn",
    "phone_number": "+256712345678",
    "description": "Payment description",
    "created_at": "2025-08-20T15:18:48.000000Z",
    "updated_at": "2025-08-20T15:18:48.000000Z"
  },
  "collection": {
    "provider": "mtn",
    "phone_number": "+256712345678",
    "amount": {
      "formatted": "10,000.00",
      "raw": 10000,
      "currency": "UGX"
    },
    "mode": "mtnuganda",
    "provider_transaction_id": "MTN_FINANCIAL_TRANSACTION_ID"
  }
}

Card payments callback

Card collections use the same callback format as mobile money. When you provide a callback_url on a card collection, we send an HTTP POST to that URL with a JSON body once the card payment is completed or failed (after the customer returns from the card gateway). The payload structure is identical to the collection examples above; only the provider and some optional fields differ.

Card-specific details

  • provider is "card payments" (lowercase)
  • phone_number may be null (card collections do not require a phone number)
  • provider_transaction_id is the gateway transaction ID from the card provider when available
  • event_type is collection.completed or collection.failed as for other collections

Example: Card payment completed

{
  "event_type": "collection.completed",
  "transaction": {
    "uuid": "transaction-uuid",
    "reference": "b59d3d6d-5827-41ee-b455-18dd20ef1c8a",
    "status": "completed",
    "amount": {
      "formatted": "5,000.00",
      "raw": 5000,
      "currency": "UGX"
    },
    "provider": "card payments",
    "phone_number": null,
    "description": "Order payment",
    "created_at": "2025-08-20T15:18:48.000000Z",
    "updated_at": "2025-08-20T15:19:02.000000Z"
  },
  "collection": {
    "provider": "card payments",
    "phone_number": null,
    "amount": {
      "formatted": "5,000.00",
      "raw": 5000,
      "currency": "UGX"
    },
    "mode": "card paymentsuganda",
    "provider_transaction_id": "PROVIDER_TRANSACTION_ID"
  }
}

Example: Card payment failed

{
  "event_type": "collection.failed",
  "transaction": {
    "uuid": "transaction-uuid",
    "reference": "b59d3d6d-5827-41ee-b455-18dd20ef1c8a",
    "status": "failed",
    "amount": { "formatted": "5,000.00", "raw": 5000, "currency": "UGX" },
    "provider": "card payments",
    "phone_number": null,
    "description": "Order payment",
    "created_at": "2025-08-20T15:18:48.000000Z",
    "updated_at": "2025-08-20T15:19:02.000000Z"
  },
  "collection": {
    "provider": "card payments",
    "phone_number": null,
    "amount": { "formatted": "5,000.00", "raw": 5000, "currency": "UGX" },
    "mode": "card paymentsuganda",
    "provider_transaction_id": null
  }
}

Your callback endpoint should accept POST with Content-Type: application/json and return HTTP 200. Use event_type and transaction.status to determine success or failure; use transaction.reference to match the collection to your order.

Disbursement Object Fields

  • provider_transaction_id: A unified field containing the transaction ID from the payment provider. This single field works for both MTN and Airtel - it automatically contains the correct provider transaction ID based on which provider processed the disbursement. The field is included in the callback when the provider transaction ID is available.
  • provider: The payment provider used (mtn or airtel)
  • phone_number: The recipient phone number
  • recipient_name: The name of the recipient
  • amount: The transaction amount with formatted and raw values
  • mode: The transaction mode (e.g., mtnuganda, airteluganda, sandbox)

Successful Disbursement Payload (Airtel Example)

{
  "event_type": "disbursement.completed",
  "transaction": {
    "uuid": "transaction-uuid",
    "reference": "transaction-reference",
    "provider_reference": null,
    "status": "completed",
    "amount": {
      "formatted": "1,000.00",
      "raw": 1000,
      "currency": "UGX"
    },
    "provider": "airtel",
    "phone_number": "+256759983853",
    "recipient_name": "Katende Nicholas",
    "description": "Send Money to Katende Nicholas",
    "created_at": "2025-12-07T05:41:28.000000Z",
    "updated_at": "2025-12-07T05:42:05.000000Z"
  },
  "disbursement": {
    "provider": "airtel",
    "phone_number": "+256759983853",
    "amount": {
      "formatted": "1,000.00",
      "raw": 1000,
      "currency": "UGX"
    },
    "mode": "airteluganda",
    "provider_reference": null,
    "recipient_name": "Katende Nicholas",
    "provider_transaction_id": "AIRTEL_MONEY_ID"
  }
}

Successful Disbursement Payload (MTN Example)

{
  "event_type": "disbursement.completed",
  "transaction": {
    "uuid": "transaction-uuid",
    "reference": "transaction-reference",
    "provider_reference": null,
    "status": "completed",
    "amount": {
      "formatted": "1,000.00",
      "raw": 1000,
      "currency": "UGX"
    },
    "provider": "mtn",
    "phone_number": "+256712345678",
    "recipient_name": "John Doe",
    "description": "Send Money to John Doe",
    "created_at": "2025-12-07T05:41:28.000000Z",
    "updated_at": "2025-12-07T05:42:05.000000Z"
  },
  "disbursement": {
    "provider": "mtn",
    "phone_number": "+256712345678",
    "amount": {
      "formatted": "1,000.00",
      "raw": 1000,
      "currency": "UGX"
    },
    "mode": "mtnuganda",
    "provider_reference": null,
    "recipient_name": "John Doe",
    "provider_transaction_id": "MTN_FINANCIAL_TRANSACTION_ID"
  }
}

Failed Disbursement Payload

{
  "event_type": "disbursement.failed",
  "transaction": {
    "uuid": "transaction-uuid",
    "reference": "transaction-reference",
    "provider_reference": null,
    "status": "failed",
    "amount": {
      "formatted": "1,000.00",
      "raw": 1000,
      "currency": "UGX"
    },
    "provider": "airtel",
    "phone_number": "+256759983853",
    "recipient_name": "Katende Nicholas",
    "description": "Send Money to Katende Nicholas",
    "created_at": "2025-12-07T05:41:28.000000Z",
    "updated_at": "2025-12-07T05:42:05.000000Z"
  },
  "disbursement": {
    "provider": "airtel",
    "phone_number": "+256759983853",
    "amount": {
      "formatted": "1,000.00",
      "raw": 1000,
      "currency": "UGX"
    },
    "mode": "airteluganda",
    "provider_reference": null,
    "recipient_name": "Katende Nicholas",
    "provider_transaction_id": "AIRTEL_MONEY_ID"
  }
}

Best Practices

Always respond with HTTP 200 to acknowledge receipt of the callback

Implement idempotency to handle duplicate callbacks

Use the transaction reference to track and verify payments

Set up proper error handling and logging for callback processing

Chat on WhatsApp