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.
Before you start
Kashier only sends a transfer webhook when all three of these are in place. If any one is missing, the transfer still goes through, but no webhook is delivered for it.
- An API key named
transfer. In the Kashier dashboard, create an API key and name it exactlytransfer, in the same mode you create transfers in — a test-mode key for test transfers, a live-mode key for live transfers. This is your transfer API key: Kashier signs every payout webhook with it, and you use it to verify the signature. It is a separate key from your Payment API Key. - A webhook URL. Register the endpoint that should receive transfer events — see manage your webhooks.
- A
merchantTransferIdon the transfer. Send your own unique reference inmerchantTransferIdwhen you create the transfer. A transfer created without it never triggers a webhook. For batches, the equivalent field ismerchantBatchId.
No merchantTransferId, no webhook
merchantTransferId is optional for creating a transfer, but required for receiving its webhook. If you rely on webhooks to track payouts, send it on every transfer.
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 (the key named transfer) 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 not a provider result
On the initiation webhook, transferResponseCode is always "00" with a generic success message. It only confirms that Kashier accepted the transfer, not that the provider paid it. 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.