> ## Documentation Index
> Fetch the complete documentation index at: https://docs.splashpay.co.tz/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive real-time notifications for disbursement events from SplashPay.

# Webhooks

SplashPay sends webhook notifications whenever the status of a disbursement changes.

Instead of continuously polling the Status API, configure a webhook endpoint to receive real-time updates for successful, failed, or cancelled disbursements.

***

## Configure a Webhook

You can configure your webhook URL from the SplashPay Merchant Dashboard or when creating your application.

Your endpoint must:

* Be publicly accessible over HTTPS
* Accept `POST` requests
* Return an HTTP `2xx` response within **30 seconds**

Example webhook URL:

```text theme={null}
https://merchant.example.com/api/webhooks/splashpay
```

***

## Headers

Every webhook request includes the following headers.

| Header                | Description            |
| --------------------- | ---------------------- |
| X-Webhook-Event       | Event type             |
| X-SplashPay-Signature | HMAC SHA-256 signature |
| X-SplashPay-Timestamp | Event timestamp        |

Example

```http theme={null}
POST /api/webhooks/splashpay HTTP/1.1

Content-Type: application/json
X-Webhook-Event: disbursement.success
X-SplashPay-Timestamp: 2026-07-05T14:25:40Z
X-SplashPay-Signature: 8b0f8eb9a6c5a7c6...
```

***

## Events

SplashPay currently sends the following disbursement events.

| Event                    | Description                  |
| ------------------------ | ---------------------------- |
| `disbursement.success`   | Funds successfully delivered |
| `disbursement.failed`    | Disbursement failed          |
| `disbursement.cancelled` | Disbursement cancelled       |

***

## Example Payload

```json theme={null}
{
  "event": "disbursement.success",
  "created_at": "2026-07-05T14:25:40Z",
  "data": {
    "reference": "PAYOUT-100001",
    "provider_reference": "S20618298751",
    "amount": "50000.00",
    "currency": "TZS",
    "status": "success",
    "provider": "selcom",
    "channel": "MOBILE_MONEY",
    "network": "MPESA",
    "phone": "255747123456",
    "metadata": {
      "employee_id": "EMP-1001"
    }
  }
}
```

***

## Verify Webhook Signature

Always verify the webhook signature before processing the payload.

The signature is generated using your **Webhook Secret**.

Algorithm:

```text theme={null}
HMAC SHA-256
```

Pseudo code

```text theme={null}
signature = HMAC_SHA256(
    webhook_secret,
    raw_request_body
)
```

Compare the generated signature with the value in the `X-SplashPay-Signature` header.

***

## Laravel Example

```php theme={null}
public function handle(Request $request)
{
    // Raw request body
    $payload = file_get_contents('php://input');

    // Headers
    $signature = $request->header('X-SplashPay-Signature');
    $timestamp = $request->header('X-SplashPay-Timestamp');
    $event = $request->header('X-Webhook-Event');

    // Verify signature
    if (!$this->verifySignature($payload, $signature)) {
        return response()->json([
            'message' => 'Invalid signature'
        ], 400);
    }

    $data = json_decode($payload, true);

    switch ($event) {

        case 'disbursement.success':

            // Mark payout as completed

            break;

        case 'disbursement.failed':

            // Mark payout as failed

            break;

        case 'disbursement.cancelled':

            // Mark payout as cancelled

            break;
    }

    return response()->json([
        'status' => 'ok'
    ]);
}
```

***

## Signature Verification Example

```php theme={null}
protected function verifySignature(
    string $payload,
    string $signature
): bool {

    $expected = hash_hmac(
        'sha256',
        $payload,
        config('services.splashpay.webhook_secret')
    );

    return hash_equals(
        $expected,
        $signature
    );
}
```

***

## Response

Your endpoint should return an HTTP **200 OK** response after successfully processing the webhook.

Example

```json theme={null}
{
    "status": "ok"
}
```

***

## Retry Policy

If SplashPay does not receive a successful **2xx** response, the webhook will be retried using an exponential backoff strategy.

Typical retry schedule:

| Attempt | Delay       |
| ------- | ----------- |
| 1       | Immediately |
| 2       | 1 minute    |
| 3       | 5 minutes   |
| 4       | 15 minutes  |
| 5       | 30 minutes  |
| 6       | 1 hour      |

***

## Best Practices

<Steps>
  <Step title="Verify every signature">
    Never trust incoming webhook requests without verifying the signature.
  </Step>

  <Step title="Respond quickly">
    Return an HTTP 200 response as soon as possible. Perform heavy processing asynchronously.
  </Step>

  <Step title="Handle duplicate events">
    Webhook deliveries may be retried. Process events idempotently using the transaction reference.
  </Step>

  <Step title="Log webhook requests">
    Log incoming payloads and headers for troubleshooting and auditing.
  </Step>
</Steps>
