Transaction callback (Deposits and Withdrawals)#
Once a merchant has been enabled for customer payments within the XGateway system, incoming customer deposits are processed automatically. To keep you informed, a callback (webhook) is sent once a transaction reaches its final state - confirmed or failed.
Merchants can also request to receive processing-type callbacks. This may be useful in certain cases, for example, to notify users that processing started, but handling these callbacks is not required for proper transaction processing, and they are not enabled by default.
Prefer callbacks over polling; they are signed and retried. For high-risk operations, confirm state via API (see the risks and details on the Callbacks Handling page). API responses are the source of truth.
Set up the transaction callback endpoint#
To accept the KYC status updates you should configure the endpoint on your side and provide it during the integration setup.
It is always best to ensure that your callback endpoint is up and running so that the notifications can reach you. XGateway will retry failed requests up to 10 times.
All notifications (delivered or not) will be available on your dashboard. You can also see the error messages for failed notifications and investigate the issue on your side.
Crypto transactions are permanently recorded on the public blockchain as well as stored in our database. This data may be used for cross check and debugging.
Callback endpoint#
https://your-callback-url-here.com/handle-transaction This endpoint must properly handle deposit callbacks. If withdrawals requested - the same endpoint will be used to send the withdrawal transaction status, see the payload examples below.
Callback payload (deposits and withdrawals)#
The amount is sent as a string to avoid unintended transformations during hash encoding and decoding.
Callback body breakdown
| Name | Type | Description |
|---|---|---|
| callbackType | "transaction" | The type of a callback |
| amount | String | The transaction amount how it was sent by the customer |
| applicationId | String | The unique application ID in the merchant system |
| currency | String | The currency sent by the customer. A full list of supported currencies can be found on the supported currencies page |
| customerId | String | The unique customer ID in the merchant system |
| hash | String | The base64-encoded hash is generated using the sha512 algorithm and includes the transaction ID, customer ID, amount, currency, and secret key The string that will be hashed: |
| id | String | The unique transaction ID in the XGateway system |
| invoiceId | String | The unique invoice ID in the XGateway system |
| network | String | The blockchain network the transaction was made in |
| orderId | String | The unique order ID in the merchant system |
| status | String | The transaction status |
| transactionHash | String | The transaction hash in a blockchain |
| type | String | The transaction type |
| info.exchangeRate | String | The exchange rate used for the conversion from fiat currency to crypto currency, if applicable |
| info.referenceAmount | String | The transaction amount in the reference currency set during the sign-up process |
| info.referenceCurrency | String | The reference currency set during the sign-up process |
| info.referenceExchangeRate | String | The exchange rate used for the conversion from transaction currency to the reference currency |
| info.transactionAmount | String | The transaction amount received by the merchant after swaps applied |
| info.transactionCurrency | String | The transaction currency received by the merchant |
| info.invoiceBaseAmount | String | The amount how it sent in invoice |
| info.invoiceBaseCurrency | String | The currency how it sent in invoice |
| info.transactionAmountBaseCurrency | String | The actual transaction amount converted into currency of the invoice |
| fees.processing | String | Processing fee in transaction currency |
| fees.technical | String | Technical fee in transaction currency |
| senderDetails.senderIban | String | The sender IBAN, used to initiate the deposit. The entire block is sent only for Fiat to Crypto deposits |
| additionalParams.utr | String | UTR for UPI payments |
Here is an example of how the different currencies work. Let's say a merchant operates in USD (reference currency). A customer sends 10 EUR through SEPA Secure with a conversion to USDT on Polygon. In this case the callback will contain:
- Amount and currency field will contain 10 and EUR.
- Reference currency USD and amount of the transaction as 10 EUR converted to USD.
info.transactionAmountandinfo.transactionCurrencywill contain the amount inpUSDTafter swap topUSDT.
Callback example: deposit#
The callback you get after a confirmed or failed Deposit transaction might look like this:
Deposit callback
{
"callbackType": "transaction",
"amount": "200",
"applicationId": null,
"currency": "EUR",
"customerId": "000394",
"hash": "rpn1za+9YmGDKSZBMVBd...i8DjYOhiq51g==",
"id": "123486c2-4dbd-4a72-8be2-3338bef9a696",
"invoiceId": "edef7683-a900-4e6a-8973-24949910017c",
"network": null,
"orderId": null,
"status": "confirmed",
"transactionHash": "0x58f6",
"type": "deposit",
"info": {
"exchangeRate": "1.03759",
"referenceAmount": "207.52",
"referenceCurrency": "USD",
"referenceExchangeRate": "1.000065",
"transactionAmount": "207.51518",
"transactionCurrency": "tUSDT",
"invoiceBaseAmount": "200",
"invoiceBaseCurrency": "EUR",
"transactionAmountBaseCurrency": "199.98"
},
"fees": {
"processing": "7.263031",
"technical": null
},
"eur": "200.12",
"usd": "207.52",
"senderDetails": {
"senderIban": "GB29NWBK60161331926819",
"senderAccountNumber": "31926819",
"senderSortCode": "601613"
},
"additionalParams": {
"utr": "111111111111"
},
"customer": {
"email": "string",
"fullname": "string"
},
"billingAddress": {
"city": "string",
"state": "string",
"postalCode": "string",
"addressLine": "string",
"countryCode": "string"
},
"paymentMethodDetails": {
"cardType": "string",
"cardExpiryYear": "string",
"cardholderName": "string",
"cardExpiryMonth": "string",
"customerAccountNumber": "string",
"cardIssuingCountryCode": "string"
},
"rejectionReason": null
}
Callback example: withdrawal#
The callback you get after a confirmed or failed Withdraw transaction might look like this:
Withdrawal callback
{
"callbackType": "transaction",
"senderDetails": null,
"amount": "1.71",
"applicationId": null,
"currency": "EUR",
"customerId": "sepa-secure-wilson",
"hash": "FA5h8Ff4Onr...Ms7T2fjN/JybzwwMAjgg6g==",
"id": "1234c71f-70fa-407b-b532-c5a219d3eb74",
"invoiceId": "edefc865-2513-4409-a944-133b06011b4d",
"network": null,
"orderId": "order_test_prod",
"status": "confirmed",
"transactionHash": null,
"type": "withdrawal",
"createdAt": "2025-07-18T07:45:18.732Z",
"updatedAt": "2025-07-18T07:46:38.611Z",
"info": {
"exchangeRate": "1.162371703223256733",
"referenceAmount": "2",
"referenceCurrency": "USD",
"referenceExchangeRate": "1.000065",
"transactionAmount": "2",
"transactionCurrency": "tUSDT",
"invoiceBaseAmount":"1.71",
"invoiceBaseCurrency":"EUR",
"transactionAmountBaseCurrency":"1.92"
},
"additionalParams": {
"utr": "111111111111"
},
"fees": {
"processing": "0.08",
"technical": null
},
"eur": "1.72",
"usd": "2"
}
Response and data handling#
You may handle the payload information as needed. You do not need to send anything back other than a success status to confirm that the callback has been received and processed on your side.
FYI: Transaction Statuses#
Below are the statuses - not an exhaustive list - of transactions supported in the system. Note that callbacks are sent only for processing (if explicitly enabled during the merchant setup), confirmed and failed transaction statuses. Other statuses are listed for informational purpose. They exist in the system and displayed in Merchant Back Office.
Transaction Statuses
| Status | Description |
|---|---|
| confirmed | The transaction has been successfully processed |
| created | The transaction has been initiated but is awaiting further processing |
| failed | The transaction has failed |
| processing | The transaction is currently being processed |
| misdirected | The transaction has been processed but cannot be linked to an existing invoice |
| on_hold_review | The transaction is under review for additional checks to prevent potential risks |
| manually_approved | The transaction has been manually reviewed and is awaiting further processing |
| manually_rejected | The transaction has been manually rejected due to identified vulnerabilities |