In rare cases, such as network retries, read timeouts, processing delays, or delivery failures, Cashfree may send the same webhook more than once for the same event. To prevent unintended side effects, implement idempotency (ensuring a duplicate webhook produces the same result as processing it once) in your webhook handler to handle duplicate deliveries.
Webhook signature
Cashfree includes the webhook signature in the request header. The following is a sample header from a webhook request.| Header name | Header value |
|---|---|
| content-length | 1099 |
| x-webhook-attempt | 1 |
| content-type | application/json |
| x-webhook-signature | 07r5C3VMwsGYeldGOCYxe5zoHhIN1zLfa8O0U/yngHI= |
| x-webhook-timestamp | 1746427759733 |
| x-webhook-version | 2025-01-01 |
Always capture the webhook payload in its raw text format before parsing into JSON. Parsing and re-serialising the payload can change the structure (for example, array ordering, spacing, or
null handling) and cause a signature mismatch during verification. Use the exact raw body string from the request when computing the signature.Verifying the signature is mandatory before processing any webhook. Refer to Signature Verification for more details.
Transfer webhook events
Cashfree triggers the following events at different stages of a wallet transfer:| Event | Description |
|---|---|
| PPI_TRANSFER_SUCCESS | The transfer is successfully credited to the beneficiary’s account. |
| PPI_TRANSFER_FAILED | The transfer cannot be processed due to an error or rejection by the bank. |
| PPI_TRANSFER_REVERSED | The beneficiary bank returns the transfer after initial processing. |
| PPI_TRANSFER_REJECTED | Cashfree rejects the transfer before it reaches the bank due to a validation failure. |
PPI_TRANSFER_SUCCESS
PPI_TRANSFER_SUCCESS
{
"event_type": "PPI_TRANSFER_SUCCESS",
"event_time": "2026-05-18T10:00:45Z",
"data": {
"user_id": "usr_001",
"wallet_id": "wlt_001",
"cf_transfer_id": "1003",
"transfer_id": "txn_ext_003",
"amount": 500.00,
"transfer_mode": "IMPS",
"actual_mode": "IMPS",
"sub_wallet": {
"cf_sub_wallet_id": "swlt_001",
"name": "Primary Wallet",
"type": "PRIMARY",
"status": "ACTIVE",
"balance": 4500.00,
"available_balance": 4500.00,
"funds_on_hold": 0.00
},
"status": "SUCCESS",
"status_code": "ACKNOWLEDGED_VIA_BENE_BANK",
"bank_reference_number": "BNKREF789012",
"bene_details": {
"bene_id": "bene_001",
"bene_instrument_id": "instr_001"
},
"purpose": "salary",
"remarks": "Monthly salary",
"initiated_at": "2026-05-18T10:00:00Z",
"processed_at": "2026-05-18T10:00:45Z",
"notes": null
}
}
PPI_TRANSFER_FAILED
PPI_TRANSFER_FAILED
{
"event_type": "PPI_TRANSFER_FAILED",
"event_time": "2026-05-18T10:00:01Z",
"data": {
"user_id": "usr_001",
"wallet_id": "wlt_001",
"cf_transfer_id": "1004",
"transfer_id": "txn_ext_004",
"amount": 500.00,
"transfer_mode": "IMPS",
"actual_mode": null,
"sub_wallet": {
"cf_sub_wallet_id": "swlt_001",
"name": "Primary Wallet",
"type": "PRIMARY",
"status": "ACTIVE",
"balance": 5000.00,
"available_balance": 5000.00,
"funds_on_hold": 0.00
},
"status": "FAILED",
"status_code": "INSUFFICIENT_BALANCE",
"bank_reference_number": null,
"bene_details": {
"bene_id": "bene_001",
"bene_instrument_id": "instr_001"
},
"purpose": "salary",
"remarks": "Monthly salary",
"initiated_at": "2026-05-18T10:00:00Z",
"processed_at": "2026-05-18T10:00:01Z",
"notes": null
}
}
PPI_TRANSFER_REVERSED
PPI_TRANSFER_REVERSED
{
"event_type": "PPI_TRANSFER_REVERSED",
"event_time": "2026-05-18T10:05:00Z",
"data": {
"user_id": "usr_001",
"wallet_id": "wlt_001",
"cf_transfer_id": "1005",
"transfer_id": "txn_ext_005",
"amount": 500.00,
"transfer_mode": "IMPS",
"actual_mode": "IMPS",
"sub_wallet": {
"cf_sub_wallet_id": "swlt_001",
"name": "Primary Wallet",
"type": "PRIMARY",
"status": "ACTIVE",
"balance": 5000.00,
"available_balance": 5000.00,
"funds_on_hold": 0.00
},
"status": "REVERSED",
"status_code": "RETURNED_FROM_BENE",
"bank_reference_number": "BNKREF_REV001",
"bene_details": {
"bene_id": "bene_001",
"bene_instrument_id": "instr_001"
},
"purpose": "salary",
"remarks": "Monthly salary",
"initiated_at": "2026-05-18T10:00:00Z",
"processed_at": "2026-05-18T10:05:00Z",
"notes": null
}
}
PPI_TRANSFER_REJECTED
PPI_TRANSFER_REJECTED
{
"event_type": "PPI_TRANSFER_REJECTED",
"event_time": "2026-05-18T10:00:01Z",
"data": {
"user_id": "usr_001",
"wallet_id": "wlt_001",
"cf_transfer_id": "1006",
"transfer_id": "txn_ext_006",
"amount": 500.00,
"transfer_mode": "IMPS",
"actual_mode": null,
"sub_wallet": {
"cf_sub_wallet_id": "swlt_001",
"name": "Primary Wallet",
"type": "PRIMARY",
"status": "ACTIVE",
"balance": 5000.00,
"available_balance": 5000.00,
"funds_on_hold": 0.00
},
"status": "REJECTED",
"status_code": "BENE_BLACKLISTED",
"bank_reference_number": null,
"bene_details": {
"bene_id": "bene_001",
"bene_instrument_id": "instr_001"
},
"purpose": "salary",
"remarks": "Monthly salary",
"initiated_at": "2026-05-18T10:00:00Z",
"processed_at": "2026-05-18T10:00:01Z",
"notes": null
}
}
Transfer webhook payload fields
The webhook payload contains important metadata in its top-level fields.| Field | Type | Description |
|---|---|---|
event_type | string | Type of transfer event: PPI_TRANSFER_SUCCESS, PPI_TRANSFER_FAILED, PPI_TRANSFER_REVERSED, or PPI_TRANSFER_REJECTED |
event_time | string | The UTC timestamp when the event occurred, in ISO 8601 format |
data | object | Transfer-specific event details |
Transfer data fields
Transfer webhook events contain the following fields insidedata:
| Field | Type | Description |
|---|---|---|
user_id | string | Identifier of the user who initiated the transfer |
wallet_id | string | Identifier of the wallet from which the transfer was made |
transfer_id | string | Your unique identifier for the transfer transaction |
cf_transfer_id | string | Cashfree’s unique identifier for the transfer transaction |
amount | number | Transfer amount in INR |
transfer_mode | string | Transfer mode requested: RTGS, NEFT, IMPS, or UPI |
actual_mode | string | Transfer mode actually used to process the transaction. Set only when the transfer reaches the bank (SUCCESS or REVERSED); null otherwise |
sub_wallet | object | Details of the sub-wallet from which funds were transferred |
sub_wallet.cf_sub_wallet_id | string | Cashfree’s unique identifier for the sub-wallet |
sub_wallet.name | string | Name of the sub-wallet |
sub_wallet.type | string | Type of the sub-wallet (for example, PRIMARY, FULL_KYC_PPI, GIFT_PPI) |
sub_wallet.status | string | Current status of the sub-wallet (for example, ACTIVE, SUSPENDED) |
sub_wallet.balance | number | Total balance in the sub-wallet in INR |
sub_wallet.available_balance | number | Balance available for new transfers after accounting for funds on hold, in INR |
sub_wallet.funds_on_hold | number | Amount reserved for in-flight transfers not yet settled, in INR |
status | string | Current status of the transfer: SUCCESS, FAILED, REVERSED, or REJECTED |
status_code | string | Granular status code for the transfer (for example, ACKNOWLEDGED_VIA_BENE_BANK, INSUFFICIENT_BALANCE, RETURNED_FROM_BENE, BENE_BLACKLISTED) |
bank_reference_number | string | Bank reference number for the transaction. Set only when the transfer reaches the bank (SUCCESS or REVERSED); null otherwise |
bene_details | object | Beneficiary details for the transfer |
bene_details.bene_id | string | Your unique identifier for the beneficiary |
bene_details.bene_instrument_id | string | Your unique identifier for the beneficiary instrument |
purpose | string | Purpose of the transfer |
remarks | string | Additional remarks for the transfer |
notes | object | null | Custom key-value pairs provided during transfer creation. Returns null if no notes were set. |
initiated_at | string | Timestamp when the transfer was initiated |
processed_at | string | Timestamp when the transfer reached a terminal state |