KashierDevelopersKashier Developers
Payouts

Payouts webhook

Webhook notifications sent during payout transfers

Kashier sends two webhooks during a transfer:

  1. Transfer initiation — fired when the transfer is accepted.
  2. Transfer final status — fired when it settles or fails.

Authentication

To verify the integrity of data sent from Kashier, you need to validate the x-kashier-signature header using HMAC SHA256.

Verification process

Extract the signatureKeys array from the payload — it lists the keys used to create the signature. Concatenate their values from the payload in the exact order the array lists them, as key_1=value_1&key_2=value_2.... Do not sort the array, and do not URL-encode the values: use them raw. Generate an HMAC SHA256 hash of that string using your transfer API key as the secret, then compare it with the x-kashier-signature header value. If they match, the data is authentic and can be safely processed.

For a single transfer the order is fixed as merchantTransferId, method, amount, merchantId, status, so the signed string looks like this:

merchantTransferId=transfer12345&method=wallet&amount=10&merchantId=MID-xxx-xxx&status=INITIATED

Batch webhooks use merchantBatchId, batchId, method, amount, merchantId, status — again in that order.

Don't reuse your payment webhook verifier

The payment webhook sorts signatureKeys alphabetically, URL-encodes each value, and is keyed by your Payment API Key. The payout webhook does none of that: array order, raw values, transfer API key. A verifier written for one produces the wrong hash for the other.

Webhook types

All webhooks include a signature header for security verification:

  • Header name: x-kashier-signature
  • The signature covers only the fields listed in the payload's signatureKeys array, joined as key=value&key=value in array order with raw values and hashed with your transfer API key — see Verification process.

Transfer initiation webhook

Sent when a transfer is initially processed by Kashier.

HeaderValue
x-kashier-signature1cdc336099a85a8b2a8ffa901cfd55eaa5824e97efd9fc56c34544e9e24bcf7a

Payload structure

{
    "transferId": "TRS-1007931588",
    "merchantTransferId": "transfer12345",
    "amount": 10,
    "method": "wallet",
    "status": "INITIATED",
    "openForReturn": false,
    "merchantId": "MID-xxx-xxx",
    "recipientName": "Jhon Doe",
    "recipientNumber": "01356839512",
    "businessEmail": "[email protected]",
    "storeName": "kashier store",
    "creatorEmail": "[email protected]",
    "creatorName": "Sara Ahmed",
    "batch": {
        "name": "Jhon Doe",
        "id": "TRS-1007931588",
        "method": "wallet",
        "transfersCount": 1
    },
    "transferResponseCode": "00",
    "transferResponseMessage": {
        "en": "success",
        "ar": "تمت الموافقة"
    },
    "date": "2025-01-15T08:32:37.829Z",
    "signatureKeys": ["merchantTransferId", "method", "amount", "merchantId", "status"]
}

transferResponseCode 00 on INITIATED is synthetic

On the initiation webhook, transferResponseCode is hard-coded to "00" with a generic success message, regardless of what the provider returned. It is not a provider result. Only a TRANSFERRED status means the funds reached the recipient; the real provider code appears on IN_TRANSIT, TRANSFERRED, and FAILED.

Transfer final status webhook

Sent when the transfer reaches its final state (completed or failed).

HeaderValue
x-kashier-signature8b41f0d7c25e9a6413ab77f2c0d95e83b6a1c4e70f28d5931ac6e40b7d92f158

Payload structure

{
    "transferId": "TRS-1007931588",
    "amount": 10,
    "method": "wallet",
    "recipientName": "Jhon Doe",
    "recipientNumber": "01123439512",
    "merchantTransferId": "transfer12345",
    "status": "FAILED",
    "openForReturn": false,
    "merchantId": "MID-xxx-xxx",
    "businessEmail": "[email protected]",
    "storeName": "kashier store",
    "creatorEmail": "[email protected]",
    "creatorName": "Sara Ahmed",
    "batch": {
        "name": "Jhon Doe",
        "id": "TRS-1007931588",
        "method": "wallet",
        "transfersCount": 1
    },
    "transferResponseCode": "k_default",
    "transferResponseMessage": {
        "en": "A General Error Occured, please contact support.",
        "ar": ""
    },
    "date": "2025-01-15T08:32:37.929Z",
    "signatureKeys": ["merchantTransferId", "method", "amount", "merchantId", "status"]
}

Body description

KeyValue
transferIdKashier's unique identifier for the transfer
merchantTransferIdYour system's reference number
amountTransfer amount
methodPayment method (e.g., 'wallet')
statusCurrent transfer status
merchantIdYour merchant ID in Kashier's system
openForReturntrue on a TRANSFERRED transfer that the receiving wallet or bank can still send back

Status values

To configure which transfer events actually trigger a webhook delivery — as opposed to the full set of statuses a transfer can reach, listed below — see manage your webhooks.

For single transfers

KeyValue
PENDINGTransfer created, balance not yet debited. This is the first status and the one the create call returns.
INITIATEDBalance debited; the transfer is ready to send to the provider.
IN_TRANSITTransfer has started but is not yet completed.
TRANSFERREDTransfer completed. Final unless openForReturn is true.
FAILEDTransfer failed (final status). Balance is reversed.

For batch transfers

KeyValue
PENDINGBatch created, transfers not yet debited.
INITIATEDBatch transfer has been created and approved by Kashier but hasn't started.
IN_TRANSITBatch has started but is not yet completed.
TRANSFERREDTransfer is completed (final status).
PARTIALLY_TRANSFERREDSome transfers in the batch were completed, while others failed.
FAILEDTransfer failed (final status).

openForReturn

A TRANSFERRED transfer with openForReturn: true is settled but still returnable — the receiving wallet or bank can bounce it back (EBC code 8222). Treat it as not final and keep reconciling. openForReturn: false on TRANSFERRED is settled and final.

Error handling

When a transfer fails, the system provides detailed information to help identify and understand the issue:

  1. Status: the transfer status will be updated to "FAILED," indicating the operation was not successful.
  2. Error code: the transferResponseCode will include a specific code that represents the reason for the failure.
  3. Error message: the transferResponseMessage will contain a detailed description of the error in both English and Arabic, allowing for better clarity and support across different audiences.

On this page