Skip to main content
Kulmi Pay fires webhook events whenever key resources on your account change state. Each event carries a topic field that identifies what happened, and a data object containing the full serialized resource at the time of the change. This page describes every event type, when it fires, and what its payload looks like.

Event status lifecycle

Every event moves through the following states:
PENDING → PROCESSING → DELIVERED
                    ↘ FAILED
StatusMeaning
PENDINGEvent has been created and is queued for delivery
PROCESSINGKulmi Pay is actively attempting to deliver the event
DELIVEREDYour endpoint returned a 200 response
FAILEDDelivery failed — your endpoint was unreachable or returned a non-200 response
You can see the status of every event in Settings → Webhooks → Events in your dashboard, or by calling GET /api/v1/webhooks/events/.

collection_event

Fires whenever a payment invoice changes state — for example, when a customer completes an M-Pesa STK push payment or a card transaction is authorized. Enable this event with the collection_event flag on your webhook. When it fires:
  • A new invoice is created
  • The invoice state transitions (e.g. PENDINGCOMPLETE or PENDINGFAILED)
Sample payload:
{
  "topic": "collection_event",
  "data": {
    "id": "inv_abc123",
    "state": "COMPLETE",
    "value": "1500.00",
    "currency": "KES",
    "provider": "MPESA",
    "account": "254712345678",
    "api_ref": "ORDER-001",
    "created_at": "2024-01-15T10:30:00Z",
    "updated_at": "2024-01-15T10:31:45Z"
  }
}
The state field tells you whether the payment succeeded. A COMPLETE state means funds have been received into your wallet.

send_money_event

Fires whenever a disbursement payment file changes state — for example, when a bulk payout moves from PENDING to PROCESSED. Enable this event with the send_money_event flag on your webhook. When it fires:
  • A payment file is created
  • The file state transitions (e.g. PENDINGPROCESSED, PREVIEW-AND-APPROVEPENDING)
Sample payload:
{
  "topic": "send_money_event",
  "data": {
    "id": "file_xyz789",
    "state": "PROCESSED",
    "provider": "MPESA-B2C",
    "currency": "KES",
    "total_amount": "25000.00",
    "transactions_count": 10,
    "batch_reference": "PAYROLL-JAN-2024",
    "created_at": "2024-01-15T08:00:00Z",
    "updated_at": "2024-01-15T08:05:30Z"
  }
}

reversal_event

Fires when a chargeback or reversal is processed against your account. Use this event to update your internal records when a previously completed payment is reversed. Enable this event with the reversal_event flag on your webhook. When it fires:
  • A chargeback is created
  • A chargeback status changes
Sample payload:
{
  "topic": "reversal_event",
  "data": {
    "id": "cb_rev456",
    "amount": "1500.00",
    "currency": "KES",
    "status": "PROCESSED",
    "reason": "Customer dispute",
    "original_invoice_id": "inv_abc123",
    "created_at": "2024-01-16T09:00:00Z",
    "updated_at": "2024-01-16T09:10:00Z"
  }
}

subscription_event

Fires whenever a subscription changes status or completes a billing cycle. Use this event to update customer access in your application when a recurring payment succeeds or fails. Enable this event with the subscription_event flag on your webhook. When it fires:
  • A subscription is created (status: PENDING)
  • A subscription becomes active (status: ACTIVE)
  • A billing cycle completes and completed_cycles increments
  • A billing cycle fails and fail_reason is set
  • A subscription is cancelled (status: CANCELED) or completes all cycles (status: COMPLETE)
Sample payload:
{
  "topic": "subscription_event",
  "data": {
    "id": "sub_def321",
    "status": "ACTIVE",
    "plan": {
      "name": "Pro Monthly",
      "amount": "2999.00",
      "currency": "KES",
      "frequency": 1,
      "frequency_unit": "M"
    },
    "customer": {
      "email": "customer@example.com",
      "first_name": "Jane",
      "last_name": "Doe"
    },
    "completed_cycles": 1,
    "next_date": "2024-02-15",
    "fail_reason": null,
    "created_at": "2024-01-15T12:00:00Z",
    "updated_at": "2024-01-15T12:01:00Z"
  }
}

wallet_transfer_event

Fires when a transfer occurs between your Kulmi Pay wallets — for example, moving funds from a USD wallet to a KES wallet. Enable this event with the wallet_transfer_event flag on your webhook. When it fires:
  • An intra-wallet transfer is initiated or completed
Sample payload:
{
  "topic": "wallet_transfer_event",
  "data": {
    "id": "wt_ghi654",
    "from_currency": "USD",
    "to_currency": "KES",
    "amount_sent": "100.00",
    "amount_received": "13250.00",
    "exchange_rate": "132.50",
    "status": "COMPLETE",
    "created_at": "2024-01-15T14:00:00Z"
  }
}

Replay failed events

If an event fails to deliver, you can replay it from your dashboard or via the API. Replaying resends the exact original payload to your webhook endpoint. To replay from the dashboard:
  1. Go to Settings → Webhooks.
  2. Select your webhook and open the Events tab.
  3. Find the failed event and click Replay.
To replay via the API: Retrieve your events at GET /api/v1/webhooks/events/, identify the event by its id, and trigger a replay from the dashboard. Events retain their original payload regardless of how many times they are replayed.
Replaying an event does not create a new event record — it resends the existing event. Make sure your event handler is idempotent so that processing the same event twice does not cause duplicate side effects in your system.