Payouts webhook
Webhook notifications sent during payout transfers
Kashier sends two webhooks during a transfer:
- Transfer initiation — fired when the transfer is accepted.
- 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=INITIATEDBatch 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
signatureKeysarray, joined askey=value&key=valuein 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.
| Header | Value |
|---|---|
| x-kashier-signature | 1cdc336099a85a8b2a8ffa901cfd55eaa5824e97efd9fc56c34544e9e24bcf7a |
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).
| Header | Value |
|---|---|
| x-kashier-signature | 8b41f0d7c25e9a6413ab77f2c0d95e83b6a1c4e70f28d5931ac6e40b7d92f158 |
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
| Key | Value |
|---|---|
| transferId | Kashier's unique identifier for the transfer |
| merchantTransferId | Your system's reference number |
| amount | Transfer amount |
| method | Payment method (e.g., 'wallet') |
| status | Current transfer status |
| merchantId | Your merchant ID in Kashier's system |
| openForReturn | true 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
| Key | Value |
|---|---|
| PENDING | Transfer created, balance not yet debited. This is the first status and the one the create call returns. |
| INITIATED | Balance debited; the transfer is ready to send to the provider. |
| IN_TRANSIT | Transfer has started but is not yet completed. |
| TRANSFERRED | Transfer completed. Final unless openForReturn is true. |
| FAILED | Transfer failed (final status). Balance is reversed. |
For batch transfers
| Key | Value |
|---|---|
| PENDING | Batch created, transfers not yet debited. |
| INITIATED | Batch transfer has been created and approved by Kashier but hasn't started. |
| IN_TRANSIT | Batch has started but is not yet completed. |
| TRANSFERRED | Transfer is completed (final status). |
| PARTIALLY_TRANSFERRED | Some transfers in the batch were completed, while others failed. |
| FAILED | Transfer 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:
- Status: the transfer status will be updated to "FAILED," indicating the operation was not successful.
- Error code: the
transferResponseCodewill include a specific code that represents the reason for the failure. - Error message: the
transferResponseMessagewill contain a detailed description of the error in both English and Arabic, allowing for better clarity and support across different audiences.