Webhooks
Whenever a payout is initiated, we update the merchant on the status of the transaction with the help of webhooks. Our onboarding team will share the IP address from which webhook calls will be made to the merchant endpoints. We advise the merchant to whitelist only these IP addresses from which legitimate calls from Zamp will be delivered.
IP addresses to whitelist
| Environment | IP address |
|---|---|
| Stage | 34.87.148.68 |
| Prod | 35.240.227.82 |
Signature validation
Once the merchant receives the webhook call, we first request the merchant to validate if the call is truly made by Zamp. To validate, extract the header key X-ZAMP-Signature and validate against the logic below. The message consists of a comma-separated string with values id and status of payoutSession. The result of the code block below and the value present in the X-ZAMP-Signature header should match.
const message = "merchant_uNR5Kc6a2zTdfqbLsDwxUZ_06_15,initiated;
const sha_message = CryptoJS.SHA256(`${message}:${ZAMP_SECRET}`);
const x_zamp_signature = CryptoJS.enc.Base64.stringify(sha_message);
Request body
The request body consists of transaction_type and transaction_id. The data passed in the request body is the same as that particular transaction type's GET API.
For example, if the transaction is of type payout_session, the response in the data parameter is the same result as API GET https://api.zamplabs.com/api/v1/transfer/payouts/{payouts-session-id}.
| Field | Type | Description |
|---|---|---|
| transaction_type | String | Type of entity for which the webhook is sent. |
| transaction_id | String | Unique ID of the entity for which the webhook is sent. |
| id | String | Unique ID of the entity for which the webhook is sent (same as transaction_id). |
| reference_id | String | Customer reference ID sent while executing the payout. |
| status | String | Payout status. |
| merchant_id | String | Merchant ID to which the payout belongs. |
| quote_id | String | Quote ID of the quote created for the payout. |
| corridor_id | String | Corridor ID (source to destination currency mapping ID). |
| source_currency_code | String | Source account currency code. |
| destination_currency_code | String | Destination currency code. |
| beneficiary_id | String | Beneficiary ID of the beneficiary to whom the payout is made. |
| source_amount | Float | Source amount in source currency. |
| receiving_amount | Float | Receiving amount in destination currency code. |
| exchange_rate | Float | Exchange rate from source to destination currency at the time when the quote was created. |
| total_fees | Float | Total fees charged for that transaction. |
| fixed_cost_currency | String | Currency code in which fees are deducted. |
| unique_transaction_reference | String | Unique transaction reference. Transaction hash for crypto payout and bank reference ID for fiat payout. |
| created_at | Timestamp | Payout created timestamp. |
| updated_at | Timestamp | Payout updated timestamp. |
For example, if the transaction is of type payout_session, the response in the data parameter is the same result as API GET https://api.zamplabs.com/transfer/v1/payouts/{payouts-session-id}.
Example payloads
Succeeded payout
{
"transaction_type": "payout_session",
"transaction_id": "iihr42_z9oFU3w5EQEtiZbVspr7WP_06_02",
"data": {
"id": "iihr42_z9oFU3w5EQEtiZbVspr7WP_06_02",
"reference_id": "ref_098fe343",
"status": "succeeded",
"merchant_id": "iihr42_2pyNY8ZxjZGLbU7i2HzNDf_04_03",
"quote_id": "iihr42_HVLTQgQJTSahdqSmSXrHeF_06_02",
"corridor_id": "iihr42_Vm6YbkiPJjuNySeoRdoBp8_05_17",
"source_currency_code": "USD",
"destination_currency_code": "USD",
"beneficiary_id": "iihr42_B9XkvSN47U9kW3UDZKVDcg_05_17",
"source_amount": 100.00,
"receiving_amount": 99.50,
"exchange_rate": 1,
"total_fees": 0.5,
"fixed_cost_currency":"USD",
"unique_transaction_reference": "4JLXHTvmYWy36CY51dCDYs1FjiTgcQys9eJabHPb27NofwZWmjKwXQD9akwWPJ6Z65n91Zue96txr5SQRWXP86kY",
"created_at": "2023-06-02T07:19:48.351663Z",
"updated_at": "2023-06-02T07:21:13.398543Z"
}
}
Failed payout
{
"transaction_type": "payout_session",
"transaction_id": "iihr42_z9oFU3w5EQEtiZbVspr7WP_06_02",
"data": {
"id": "iihr42_z9oFU3w5EQEtiZbVspr7WP_06_02",
"reference_id": "ref_098fe343",
"status": "failed",
"merchant_id": "iihr42_2pyNY8ZxjZGLbU7i2HzNDf_04_03",
"quote_id": "iihr42_HVLTQgQJTSahdqSmSXrHeF_06_02",
"corridor_id": "iihr42_Vm6YbkiPJjuNySeoRdoBp8_05_17",
"source_currency_code": "USD",
"destination_currency_code": "USD",
"beneficiary_id": "iihr42_B9XkvSN47U9kW3UDZKVDcg_05_17",
"source_amount": 100.00,
"receiving_amount": 99.50,
"exchange_rate": 1,
"total_fees": 0.5,
"fixed_cost_currency":"USD",
"unique_transaction_reference": "",
"created_at": "2023-06-02T07:19:48.351663Z",
"updated_at": "2023-06-02T07:21:13.398543Z"
}
}
Expected response and retries
We expect the merchant to return an HTTP 200 OK status when the message has been received. In the absence of an HTTP 200 OK response, Zamp retries the webhook with exponential backoff and doubling delay. Retries continue for a maximum of 24 hours.