{"activeVersionTag":"latest","latestAvailableVersionTag":"latest","collection":{"info":{"_postman_id":"314c91b8-624c-4189-a3bb-7bd5331329a0","name":"Heropayments API deposits/payouts flow","description":"Here you can find the workflow and detailed explanation of API requests that let you process crypto payments on your platform.\n\n### **We are partnered with cashiers like:**\n\n- Devcode/Payments IQ: [https://payments-iq.com](https://payments-iq.com)\n    \n- Praxis: [https://praxis.tech](https://praxis.tech)\n    \n\nAnd others like Corefy, FXbo and etc.\n\nIf you have any questions or you want to test our solution, feel free to reach out to us via [support@heropayments.io](https://mailto:support@heropayments.io)\n\n# Authentication\n\nTo use Heropayments API, you should do the following:\n\n- Sign up at [https://app.heropayments.io/](https://app.heropayments.io/) or at [my.heropayments.io](http://app.heropayments.io/my.heropayments.io);\n    \n- Import our collection of API methods in Postman;\n    \n- Insert API keys in Postman;\n    \n- Send a request.\n    \n\n**DO NOT SHARE YOUR API/SECRET KEY WITH ANY 3RD PARTIES!**\n\n# Recommended integration flow\n\n### DEPOSIT FLOW\n\nA user wants to top up the account:\n\n1. UI - Add a \"top up with crypto\" method to your platform;\n    \n2. UI - Ask your user to choose a cryptocurrency for the deposit and specify the amount to create a deposit transaction in the user's account currency (transaction amount is optional);\n    \n3. API - To get estimated deposit amount for the selected cryptocurrency use [GET \"Get estimated price\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#434f0b85-7d59-407c-994b-4455dfef82df) and to get minimum deposit amount use [GET \"Minimum payment amount (V2)'](https://documenter.getpostman.com/view/17469357/UVyvwv7a#74245569-2457-46f7-bbce-55df4d9d4229);\n    \n4. UI - Display the minimum deposit amount to your user and inform them that a lower amount won't be processed;\n    \n5. API - To create the transaction for deposit and to generate a deposit address call the POST [\"Create a deposit (V2)\" ](https://documenter.getpostman.com/view/17469357/UVyvwv7a#412c5848-dbed-42c5-980b-a3060e9e8c46) / [\"Create a deposit (Custody)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#20b9f2a0-936b-4909-980f-9b7ca72ed6c8);\n    \n6. UI - Show the generated deposit address to your user and ask to send the payment there. Once Heropayments accepts the deposit, it will be automatically converted into your balance currency and credited to your Merchant's account in our system.\n    \n7. API - Get the transaction status either via our callbacks or manually via GET [\"Payment status check by id (V2)\" ](https://documenter.getpostman.com/view/17469357/UVyvwv7a#f850d90d-7926-46f6-bcbf-4b90369cbbf8) / [\"Payment status check by id (Custody)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#b62b5946-51fe-4d79-b818-4ff427dcf727);\n    \n8. All deposits are accumulated on your Merchant's account USDT wallet in our system. Call [GET \"Get balance\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#51715a30-cf5e-4237-8815-2d3228311cd7) method to check the balance. You can get a settlement upon the request.\n    \n\nOur [multiple deposit processing feature ](https://documenter.getpostman.com/view/17469357/UVyvwv7a#multiple-deposit-processing) increases the conversion rate of deposits. It will let your users pay twice or more to the same deposit address without visiting the cashier.\n\n[Automated mistaken deposits processing ](https://documenter.getpostman.com/view/17469357/UVyvwv7a#automated-mistaken-deposits-processing) will allow us to accept deposits if users mistakenly send a currency different from the one specified in the invoice.\n\n### WITHDRAWAL FLOW (PAYOUTS TO USERS)\n\nFirst, you can view the available balance using GET [“Get balance (V2)” ](https://documenter.getpostman.com/view/17469357/UVyvwv7a#51715a30-cf5e-4237-8815-2d3228311cd7) / [\"Get balance (Custody)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#8b8de6a1-fec5-4255-a087-1dece185ba01).\n\nTo create a withdrawal, use POST [“Create a withdrawal (V2)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#a4e3c2d9-c88d-417e-90eb-947fc8eb9c10) / [\"Create a withdrawal (Custody)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#bb5ca6c4-abbe-4bc3-b2ff-4f7425cac02a) request method. Specify the address, currency and amount for the withdrawal.\n\nYou can monitor transaction status via our callbacks or manually, using GET [\"Payment status check by id (V2)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#f850d90d-7926-46f6-bcbf-4b90369cbbf8) / [\"Payment status check by id (Custody)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#b62b5946-51fe-4d79-b818-4ff427dcf727).\n\n1. UI - Add the withdrawal with crypto option to your platform;\n    \n2. UI - Ask your user to choose a cryptocurrency for withdrawal;\n    \n3. API - Get the estimated amount for the selected currency by using [POST \"Get estimated price\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#434f0b85-7d59-407c-994b-4455dfef82df) and minimum withdrawal amount by using [\"Minimum payment amount (V2)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#74245569-2457-46f7-bbce-55df4d9d4229);\n    \n4. UI - Display the minimum withdrawal amount to your user and make clear that a lower amount won't be processed;\n    \n5. UI - Ask the user to specify a crypto wallet address to receive a withdrawal;\n    \n6. API - Call POST [\"Create a withdrawal (V2)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#a4e3c2d9-c88d-417e-90eb-947fc8eb9c10) / [\"Create a withdrawal (Custody)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#bb5ca6c4-abbe-4bc3-b2ff-4f7425cac02a);\n    \n7. API - Get the transaction status via our callbacks or manually, by calling GET [\"Payment status check by id (V2)\" ](https://documenter.getpostman.com/view/17469357/UVyvwv7a#b4efb371-d1c9-48f6-b47b-5f8622accbdc) / [\"Payment status check by id (Custody)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#b62b5946-51fe-4d79-b818-4ff427dcf727) method to receive actual information about the withdrawal request.\n    \n\n**Daily withdrawal limits:**\n\nTo elaborate on this, withdrawal limits are made to prevent situations when a merchant's account might accidentally run out of funds or overpay to a particular user.\n\nWe have 2 types of daily limits:\n\n- Merchant withdrawal limit – for the entire merchant account per day\n    \n- Customer withdrawal limit – for each customerID per day\n    \n\nTo set up the limits, reach us via [partners@heropayments.io](https://mailto:partners@heropayments.io) or contact your personal account manager.\n\n# **API Request signing**\n\nIn order to sign API requests, you will have to use your API Secret (which can be found in your admin panel - [https://app.heropayments.io/api](https://app.heropayments.io/api) or [https://my.heropayments.io/settings/api-keys)](https://my.heropayments.io/settings/api-keys) to calculate the request signature using the **HMAC-SHA512** algorithm.\n\nYou have to pass the request body as the data parameter and your API Secret as **secretKey** parameter. After calculating the signature, you have to attach it to the **x-api-sign** header.\n\n### POST\n\nFor all requests with body - you should sign body and specify it to **x-api-sign** header.\n\n### GET\n\nFor all GET requests - you should sign query string without question mark(?). For example:  \n[<code>https://api.heropayments.io/v2/method?foo=bar</code>](https://api.heropayments.io/v2/currencies?foo=bar) should be signed string `foo=bar`\n\nIf a request doesn't have a query string, you should sign an empty string:\n\n`yourSigningFunction('')`\n\nThen specify it to **x-api-sign** header.\n\n**PLEASE NOTE**\n\nTo make sure you calculate the correct signature, you have to normalize the JSON data:\n\n- it should not contain any spaces or newlines in it.\n    \n- it should not have any zero-padded numbers (from both sides) unless they're quoted strings (e.g. 001.10 is invalid, \"001.10\" is valid)\n    \n\nEasiest way to do this automatically with Javascript:  \n`JSON.stringify(JSON.parse(yourJsonString))`\n\nYou can check if the created signature is valid or not via our signature validation tool - [https://codepen.io/ethan-reynolds-9823/full/jEOVRbV](https://codepen.io/ethan-reynolds-9823/full/jEOVRbV)\n\n### Examples\n\nPostman pre-request script (V2):\n\n``` typescript\nconst crypto = require('crypto-js');\nlet payload = '';\nconst secret = pm.environment.has('api-secret')\n    ? pm.environment.get('api-secret')\n    : pm.variables.has('api-secret')\n        ? pm.variables.get('api-secret')\n        : pm.globals.has('api-secret')\n            ? pm.globals.get('api-secret')\n            : pm.collectionVariables.has('api-secret')\n                ? pm.collectionVariables.get('api-secret')\n                : console.error(new Error('api-secret is missing'));\ntry {\n    switch (pm.request.method) {\n        case 'POST': \n            payload = JSON.stringify(JSON.parse(pm.request.body.toString()));\n            break;\n        case 'GET':\n            payload = pm.request.url.query.toString();\n            break;\n    }\n} catch {\n    console.error(new Error('Cant decode JSON body'))\n}\nconsole.log('payload', payload);\nconst sign = crypto.HmacSHA512(payload, secret).toString();\npm.request.headers.upsert({\n    key: 'x-api-sign',\n    value: sign,\n});\n\n ```\n\nPostman pre-request script (Custody):\n\n``` typescript\nconst crypto = require('crypto-js');\nlet payload = '';\nconst secret = pm.environment.has('api-secret-custody')\n? pm.environment.get('api-secret-custody')\n: pm.variables.has('api-secret-custody')\n? pm.variables.get('api-secret-custody')\n: pm.globals.has('api-secret-custody')\n? pm.globals.get('api-secret-custody')\n: pm.collectionVariables.has('api-secret-custody')\n? pm.collectionVariables.get('api-secret-custody')\n: console.error(new Error('api-secret-custody is missing'));\ntry {\npayload = JSON.stringify(JSON.parse(pm.request.body.toString()));\n} catch {\nconsole.error(new Error('Cant decode JSON body'))\n}\nconst sign = crypto.HmacSHA512(payload, secret).toString();\n// console.log(secret, sign);\npm.request.headers.upsert({\nkey: 'x-api-sign',\nvalue: sign,\n});\n\n ```\n\nAn example in node.js (backend):\n\n``` typescript\nimport crypto from 'crypto';\nfunction calculateSignature (data: string | Buffer, secretKey: string | Buffer): string {\n    return crypto\n      .createHmac('SHA512', secretKey.toString())\n      .update(data.toString())\n      .digest('hex');\n}\n\n ```\n\nAn example in C#:\n\n``` csharp\nusing System;\nusing System.Security.Cryptography;\nusing System.Text;\npublic class HmacHelper\n{\n    public static string CalculateSignature(object data, object secretKey)\n    {\n        byte[] dataBytes;\n        byte[] secretKeyBytes;\n        if (data is string dataString)\n        {\n            dataBytes = Encoding.UTF8.GetBytes(dataString);\n        }\n        else if (data is byte[] dataBuffer)\n        {\n            dataBytes = dataBuffer;\n        }\n        else\n        {\n            throw new ArgumentException(\"Data must be a string or byte array\");\n        }\n        if (secretKey is string secretKeyString)\n        {\n            secretKeyBytes = Encoding.UTF8.GetBytes(secretKeyString);\n        }\n        else if (secretKey is byte[] secretKeyBuffer)\n        {\n            secretKeyBytes = secretKeyBuffer;\n        }\n        else\n        {\n            throw new ArgumentException(\"Secret key must be a string or byte array\");\n        }\n        using (var hmac = new HMACSHA512(secretKeyBytes))\n        {\n            var hashBytes = hmac.ComputeHash(dataBytes);\n            return BitConverter.ToString(hashBytes).Replace(\"-\", \"\").ToLower();\n        }\n    }\n    public static void testStrings()\n    {\n        string data = \"example data\";\n        string secretKey = \"your-secret-key\";\n        string signature = CalculateSignature(data, secretKey);\n        Console.WriteLine(\"Signature: \" + signature);\n    }\n    public static void testBytes()\n    {\n        byte[] data = System.Text.Encoding.UTF8.GetBytes(\"example data\"); // Data in buffer form\n        byte[] secretKey = System.Text.Encoding.UTF8.GetBytes(\"your-secret-key\"); // Key as a buffer\n        string signature = CalculateSignature(data, secretKey);\n        Console.WriteLine(\"Signature: \" + signature);\n    }\n    public static void Main()\n    {\n        testStrings();\n        testBytes();\n    }\n}\n\n ```\n\nAn example in PHP:\n\n``` php\n  $apiKey = 'YOUR_KEY';\n  $apiSecret = 'YOUR_SECRET';\n  $apiUrl = 'https://api.heropayments.io/v2/payments';\n  $message = json_encode(\n    array(\n        'payCurrency' => 'trx',\n        'priceCurrency' => 'usd',\n        'priceAmount' => '1000',\n        'customerId' => '123',\n        'customerEmail' => 'test@test.com',\n        'externalOrderId' => '100500',\n        'callbackUrl' => 'https://example.com/callback?orderId=100500'\n    ),\n    JSON_UNESCAPED_SLASHES\n  );\n  $sign = hash_hmac('sha512', $message, $apiSecret);\n  $requestHeaders = [\n    'x-api-key:' . $apiKey,\n    'x-api-sign:' . $sign,\n    'Content-type: application/json'\n  ];\n  $ch = curl_init($apiUrl);\n  curl_setopt($ch, CURLOPT_POST, 1);\n  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n  curl_setopt($ch, CURLOPT_POSTFIELDS, $message);\n  curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);\n  $response = curl_exec($ch);\n  curl_close($ch);\n  var_dump($response);\n\n ```\n\nAn example in Python:\n\n``` python\n #Python 3.8\nimport requests\nimport json\nimport hashlib\nimport hmac\nurl = \"https://api.heropayments.io/v2/payments\"\ndef generate_signature(data):\n  key = \"YOUR_SECRET\" \n  key_bytes= key.encode() \n  data_bytes = data.encode()\n  return hmac.new(key_bytes, data_bytes, hashlib.sha512).hexdigest()\npayload = {\n    \"payCurrency\":\"trx\",\n    \"priceCurrency\":\"usd\",\n    \"priceAmount\":1000,\n    \"customerId\":\"123\",\n    \"externalOrderId\":\"100500\",\n    \"customerEmail\":\"test@test.com\",\n    \"callbackUrl\":\"https://example.com/callback?orderId=100500\",\n    \"fiat\": True,\n}\npayload = json.dumps(payload, separators=(',', ':'))\nheaders = {\n  'x-api-key': 'YOUR_KEY',\n  'Content-Type': 'application/json',\n  'x-api-sign': generate_signature(payload)\n}\nresponse = requests.request(\"POST\", url, headers=headers, data=payload)\n\n ```\n\nAn example in browser:\n\n``` typescript\nimport hmacSHA512 from 'crypto-js/hmac-sha512';\nfunction calculateSignature (data: string | Buffer, secretKey: string | Buffer): string {\n    return hmacSHA512(data.toString(), secretKey.toString());\n}Also you can use Postman pre-request script:\n\n ```\n\n# Callbacks\n\nCallbacks are used for notifications when transaction status is changed. To use them, you should complete the following steps:\n\n1. Get the secret key in \"API\" section at the admin panel;\n    \n2. When you call POST [\"Create a deposit (V2)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#412c5848-dbed-42c5-980b-a3060e9e8c46) / [\"Create a deposit (Custody)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#20b9f2a0-936b-4909-980f-9b7ca72ed6c8) or POST [\"Сreate a withdrawal (V2)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#a4e3c2d9-c88d-417e-90eb-947fc8eb9c10) / [\"Create a withdrawal (Custody)\" ](https://documenter.getpostman.com/view/17469357/UVyvwv7a#bb5ca6c4-abbe-4bc3-b2ff-4f7425cac02a) request, insert your URL address in \"callbackUrl\" field to get notifications.\n    \n3. You will receive all the parameters at the URL address you specified in (2) by POST request. The POST request will contain x-api-sign parameter in the header. The body of the request is similar to GET [“Payment status check by id (V2)”](https://documenter.getpostman.com/view/17469357/UVyvwv7a#f850d90d-7926-46f6-bcbf-4b90369cbbf8) / [\"Payment status check by id (Custody)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#b62b5946-51fe-4d79-b818-4ff427dcf727) response body, but not identical;\n    \n4. Convert the response body to string using [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify);\n    \n5. Sign a string with a secret key with **HMAC-SHA512** algorithm;\n    \n6. Compare the signed string from the previous step with the x-api-sign stored in the header of the callback request. If these strings are identical, the system works properly. Otherwise, contact us via [support@heropayments.io](https://mailto:support@nowpayments.io) to solve the problem.\n    \n\n### Callback notification examples\n\n#### V2 deposit Callback\n\n``` javascript\n\"id\": \"f5529a76-cb68-4d19-a97b-12dcd6712c02\", // Heropayments payment ID.\n      \"fiat\": true, // parameter to specify for priceCurrency. True is set by default. Pass false to create a transaction with crypto estimation.\n      \"status\": \"finished\", // status of the transaction.\n      \"feeUSDT\": 0.02694825, // Heropayments processing fee (clientAmountUsdt * feePercent)\n\"invoice\": {\n            \"id\": \"9852d8c1-fca6-45d8-aa55-99e0050c2d94\", // invoice ID, not recommended to use it as payment identificator, as it does not appear in the report.\n            \"orderId\": \"1454445333244115\", // merchant's custom order ID.\n            \"customerId\": \"123\", //  ID of the customer.\n            \"priceAmount\": 100, //  amount specified by the user for the deposit in priceCurrency. The estimated amount to complete the payment, is not equal to the sent amount.\n            \"priceCurrency\": \"usd\" // user's account currency: fiat or cryptocurrency (USD, EUR, BTC,DOGE etc.)\n           },\n            \"payHash\": \"0x4fee71ad6617f18cfd0e75a9a349496f8bab04d4bbab44944a47376f40ead6a\", // transaction hash to the deposit address.\n            \"payRate\": 598.85, //  payCurrency to outcomeCurrency ratio.\n            \"payExtra\": null, // additional Id which is necessary for identifying a recipient of the transaction (memo, destination tag). Obligatory for deposits in XRP, XLM, HBAR.\n            \"sequence\": \"original\", // as soon as each payment has to have its own unique externalOrderId, we add an additional field \"sequence\" to differentiate the subsequent payments.\nUse case: after finishing the first payment, the customer saves the payAddress and sends the funds again to this address without creating an invoice on the site.\nExample for the first payment:\nexternalOrderID: 1254435345456456\nsequence: original\nExample for the second and further payments:\nexternalOrderID: 1254435345456456\nsequence (2nd payment): 4fb7defa35a056ecc64fc74ea97ef90f (new unique ID)\nsequence (3rd payment: 4fb7d453566545623ав2343455(new unique ID)\netc.\n        \"createdAt\": \"2025-05-02T19:56:13.000Z\", // time, when the transaction was created.\n        \"payAmount\": 0.1678591, // expected amount to pay by the user in crypto (payCurrency) to finish the transaction.\n        \"priceRate\": 0.9996, // priceCurrency to outcomeCurrency ratio.\n        “userNotes” : null, // technical field, not used.\n        \"feePercent\": 0.5, // Heropayments processing fee (%).\n        \"networkFee\": 0,5, // network fee deducted for processing the blockchain transaction.\n        \"paidAmount\": 0.009, //  amount that was actually sent in crypto (payCurrency) in the transaction.\n        \"payAddress\": \"0x2d7e23d8f9e04110d1c6d255b40841a8585d6662\", //  deposit address for crypto assets, used to process the deposit.\n        \"callbackUrl\": \"https://example.com/callback\", // HTTP(s) URL of your server which will accept callback requests. Heropayments will send callbacks whenever the status of a transaction gets an update.\n        \"outcomeHash\": \"0x4fa6f8ca917de24ddf289a997de597657f48b1c9a2f5ffae96847e8e6c36595\", //  transaction hash to the merchant’s balance wallet.\n        \"payCurrency\": \"bnbbsc\", // cryptocurrency sent by the user to finish the deposit\n        \"payHashLink\": \"https://bscscan.com/tx/0x4fee71ad664f18cfffd0e75a9a3494696f8bab0d4bbab44944a4376f40ea56a\", //  link to the transaction hash.\n        \"clientAmount\": 5.39, //  fiat equivalent of clientAmountUsdt in priceCurrency. Shows the amount to be credited to user’s account. \n        \"payinSenders\": [\n            \"0x3b3cfe3336b6accf2044274cf3c4825c4b644cb\" // customer’s wallet, used to process the deposit.\n        ],\n        \"customerEmail\": \"TestEmail.com\", // user’s email address (not required).\n        \"outcomeAmount\": 4.86270175, // amount credited to the merchant’s wallet.\n        \"outcomeAddress\": \"0x8a466fc4c95dd97d51fa5532890dea7fff0cfbf\",\n        \"externalOrderId\": \"1454445333244115\", // merchant's custom order ID\n        \"outcomeCurrency\": \"usdt20\", // merchant’s balance currency.\n        \"outcomeHashLink\": \"https://etherscan.io/token/0xdac17f958d2ee523a22006994597c13d831ec7?a=0x4fa6f8ca917de24ddf28cc9a997de597657f8b1c9a2f5ffae96847e84e6c36595\", // transaction hash link after the exchange to merchant’s wallet.\n        \"transactionType\": \"deposit\", // always deposit or withdrawal\n        \"clientAmountUsdt\": 5.38965 // deposit amount  received from the  user and converted to USDT. Calculated as “merchantAmountUsdt” + network fee + processing fee.\n        \"merchantAmountUsdt\": 4.86270175 // amount credited to Merchant’s wallet in USDT. Calculated as ClienttAmountUsdt” - network fee - processing fee.\n\n ```\n\n**V2 withdrawal Callback**\n\n``` javascript\n \"id\": \"536d0fcf-beb3-4b6b-b752-4ec5b16e02e8\", // Heropayments payment ID.\n        \"fiat\": true, // // parameter to specify priceCurrency. True is set by default. Pass false to create a transaction with crypto estimation, if the user's account is in crypto.\n        \"status\": \"finished\", //  // status of the transaction.\n        \"feeUSDT\": 0.03933199, // Heropayments processing fee (clientAmountUsdt * feePercent).\n        \"invoice\": {\n            \"id\": \"b4d98885-f83e-42d9-a5b2-fec6f868f5cb\", // invoice ID, not recommended to use it as payment identificator, as it does not appear in the report.\n            \"orderId\": null, // always null\n            \"customerId\": \"12312\", //  ID of the customer.\n            \"priceAmount\": 600, //  amount specified by the user for the withdrawal in priceCurrency. The estimated amount to complete the payment, is not equal to the sent sum.\n            \"priceCurrency\": \"rub\" // user's account currency: fiat or cryptocurrency (ex. USD, EUR, BTC,DOGE etc.)\n        \"payHash\": \"0x0f248ffd2874c0def2b15141eb1409603a9ed24a870c948d13644402f84fd\", // transaction hash from the merchant's balance address to an exchange address.\n        \"payRate\": 1, // payCurrency to outcomeCurrency ratio.\n        \"payExtra\": null, // additional Id which is necessary for identifying a recipient of the transaction. Also known as additional address, memo or destination tag. Users must enter it while withdrawing XRP, BNB, XLM and some other currencies\n        \"sequence\": \"original\", //used for deposits only, for withdrawals remains \"original\".\n        \"createdAt\": \"2025-03-06T10:57:22.000Z\", // time, when the transaction was created.\n        \"payAmount\": 6.85714285, //  the amount that must be deducted from the merchant's account to process a withdrawal.\n        \"priceRate\": 0.01086, // priceCurrency to outcomeCurrency ratio.\n        \"userNotes\": null, // technical field, not used.\n        \"feePercent\": 0.6, // Heropayments processing fee (%).\n        \"networkFee\": 0.3, // network fee deducted for processing the blockchain transaction.\n        \"paidAmount\": 6.85714285, // the amount deducted from the balance to process the payment.\n        \"callbackUrl\": \"https://webhook.site/07e09722-fc19-4d0d-b81f-f36a8c65850\", // HTTP(s) URL of your server which will accept callback requests. Heropayments will send callbacks whenever the status of a transaction gets an update.\n        \"outcomeHash\": \"0x98a839528e39a1d4c1472fcc878df6aa62f41ca5bf9ffd902035b9402cfe8f\", // transaction hash to the user's wallet\n        \"payCurrency\": \"usdtbsc\", // merchant's balance currency.\n        \"payHashLink\": \"https://bscscan.com/tx/0x0f248ffd2874c0d2b15141eb141b09603a9ed624a870c948d13644402f84fd\", // transaction hash from the merchant's balance address to an exchange address.\n        \"clientAmount\": 600, // equivalent of priceAmount in priceCurrency. This amount that user should receive for a withdrawal.\n        \"customerEmail\": \"test@test.com\", //  user's email address (not required).\n        \"outcomeAmount\": 6.516, // the amount that a user received for a withdrawal\n        \"outcomeAddress\": \"0x3b3cfe3336b6accf2044274cf3c5c25c4b64cb\", //  user's address to receive the withdrawal.\n        \"outcomeExtraId\": null, // the same as payExtra.\n        \"externalOrderId\": \"112434321\", // merchant's custom order ID.\n        \"outcomeCurrency\": \"usdtbsc\", // a currency that the user chose for the withdrawal.\n        \"outcomeHashLink\": \"https://bscscan.com/tx/0x98a839528e39a4c1472fcc878df6aa62f41ca5bb9f9ffd902035b9402cfe8f\", // transaction hash  link to the user's wallet.\n        \"transactionType\": \"withdrawal\", // always deposit or withdrawal.\n        \"clientAmountUsdt\": 6.51781086, //  USDT equivalent of a withdrawal amount sent to the user. Calculated as \"merchantAmountUsdt\" -  network fee -  processing fee.\n        \"merchantAmountUsdt\": 6.85714285 // the amount deducted from the balance to process the payment. Calculated as \"ClienttAmountUsdt\" + network fee + processing fee.\n\n ```\n\n**Custody deposit Callback**\n\n``` javascript\n\"id\": \"f3748917-d4ae-4dae-bec1-c00e269be990\", // Heropayments payment ID.\n        \"hash\": \"0xc81b15173d536f5063ec4c11cfd37aa5cfbfd668ac2433d175efea100bfd25234\", // deposit hash\n        \"amount\": \"4\", // deposited amount.\n        \"status\": \"finished\", // status of the transaction.\n        \"address\": \"0xfc9ca35f6296cadfc5f925145acd55abfd0eafae8\", //  deposit address for crypto assets.\n        \"comment\": null, //  additional information about the deposit.\n        \"currency\": \"usdtbsc\", // deposited currency.\n        \"hashLink\": null,\n        \"sequence\": \"original\", //  helps to differentiate between the subsequent payments. Please read multiple deposit processing.\n        \"subEmail\": null, //  if a payment was created by a subuser of the main account, the field reflects the name of the subaccount.\n        \"createdAt\": \"2025-03-13T14:16:53.315Z\", // time, when the deposit was created.\n        \"feeAmount\": \"0.02\", // Heropayments processing fee in the Currency equivalent.\n        \"updatedAt\": \"2025-03-13T14:21:22.163Z\", // time, when the deposit was updated.\n        \"customerId\": \"new_test_user\",\n        \"feePercent\": 0.5, //  Heropayments processing fee (%).\n        \"networkFee\": \"0.3\", // network fee deducted for processing the blockchain transaction. Estimated in a payment currency equivalent\n        \"callbackUrl\": \"https://webhook.site/07e09722-fc19-4d0d-b81f-f5236a8c65850\", //  HTTP(s) URL of your server which will accept callback requests. Heropayments will send callbacks whenever the status of a transaction gets an update. If your server is inaccessible or cannot accept a callback request, our system will try to send it again until it reaches the limit of five retries.\n        \"addressExtra\": null, // additional Id which is necessary for identifying a recipient of the transaction (memo, destination tag). Obligatory for deposits in XRP, XLM, HBAR.\n        \"clientAmount\": \"4\", // amount of crypto received from a user to a created deposit address.\n        \"confirmations\": 23, //  number of confirmations on the blockchain. We need to wait until the payment gets at least several confirmations to process it.\n        \"merchantAmount\": \"3.68\", //amount credited to Merchant's wallet in Currency\n        \"externalOrderId\": \"mercha56564554n2t_44o5rder_id_123123\", // merchant's custom order ID. Example: BGBRB-30020. This ID must be unique to create a transaction.\n        \"transactionType\": \"deposit\" // always deposit or withdrawal.\n\n ```\n\n## Fallbacks\n\nWe strongly recommend you to back up callback services by using GET [\"Payment status check by id (V2)”](https://documenter.getpostman.com/view/17469357/UVyvwv7a#f850d90d-7926-46f6-bcbf-4b90369cbbf8) / [\"Payment status check by id (Custody)\"](https://documenter.getpostman.com/view/17469357/UVyvwv7a#b62b5946-51fe-4d79-b818-4ff427dcf727) to receive the same info about payments as you get from the callback notification.\n\n## **CallbackUrl recommendations**\n\nWe highly recommend setting static callBackUrls both for deposit and withdrawals. Using static callBackUrls will facilitate the process of changing callback URLs if a need arises.\n\nExample of static callBackUrls (recommended flow)\n\n- **Deposit:** [<b>https://m54543qin7.com/deposit/hero_payments</b>](https://m54543qin7.com/deposit/hero_payments) **(for all deposit transactions one callBackUrl)**\n    \n- **Withdrawal:** [<b>https://m54543qin7.com/withdrawal/hero_payments</b>](https://m54543qin7.com/withdrawal/hero_payments) **(for all withdrawal transactions one callBackUrl)**\n    \n\nExample of dynamic callBackUrls (not recommended flow)\n\n- Deposit:  \n    [<b>https://m54543qin7.com/deposit/hero_payments</b>](https://m54543qin7.com/deposit/hero_payments)/payment_1  \n    [<b>https://m54543qin7.com/deposit/hero_payments</b>](https://m54543qin7.com/deposit/hero_payments)/payment_2  \n    [<b>https://m54543qin7.com/deposit/hero_payments</b>](https://m54543qin7.com/deposit/hero_payments)/payment_3  \n    etc.\n    \n- Withdrawal:  \n    [<b>https://m54543qin7.com/withdwaral/hero_payments</b>](https://m54543qin7.com/deposit/hero_payments)/payment_1  \n    [<b>https://m54543qin7.com/withdwaral/hero_payments</b>](https://m54543qin7.com/deposit/hero_payments)/payment_2  \n    [<b>https://m54543qin7.com/withdwaral/hero_payments</b>](https://m54543qin7.com/deposit/hero_payments)/payment_3  \n    etc.\n    \n\n# Payment Statuses - (V2 flow)\n\nThe transaction's status describes what is happening to the funds at any given moment and their current state.\n\nFor the detailed description of the statuses, please refer to the [GET \"Payment status check by id (V2)”](https://documenter.getpostman.com/view/17469357/UVyvwv7a#b4efb371-d1c9-48f6-b47b-5f8622accbdc) method.\n\n#### Deposit statuses\n\nIn progress:\n\n- waiting\n    \n- confirming\n    \n- exchanging\n    \n- hold\n    \n\nFailed or unsuccessful (suspend the transaction on your end):\n\n- failed\n    \n- refunded\n    \n- expired\n    \n\nSuccessful (complete the deposit on your end by updating the balances, etc.):\n\n- sending\n    \n- finished\n    \n\n#### Withdrawal statuses\n\nIn progress:\n\n- waiting\n    \n- confirming\n    \n- exchanging\n    \n- hold\n    \n- sending\n    \n\nFailed or unsuccessful (suspend the transaction on your end):\n\n- failed\n    \n- refunded\n    \n\nSuccess (complete the withdrawal on your end by updating the balances, etc.):\n\n- finished\n    \n\n## Faster Top-Ups with the \"Confirming\" Status\n\nHow to implement user balance top-up at the **Confirming** status?\n\nIn the confirming callback, we send the value of the `paidAmount` field — this is the actual amount of cryptocurrency the user transferred to the deposit address.\n\n### Top-up options:\n\n**1) Balance currency: USD — payment in stablecoin**  \nIf the user’s balance currency is USD and they pay in a stablecoin (USDT TRC20, USDT ERC20, etc.), you can use the `paidAmount` value and credit the equivalent amount in fiat.\n\n**2) Balance currency is not USD**  \nTo estimate how much fiat should be credited to the user, we recommend using the endpoint:  \n[https://api.heropayments.io/v2/rate](https://api.heropayments.io/v2/rate)\n\n---\n\n### Risk:\n\nSometimes a transaction does not reach the `finished` status and, after `confirming`, receives the `failed` status (meaning the funds never reached us). In this case, the user’s balance would be credited even though the funds were not actually received.\n\n### Why might a transaction not reach the `finished` status?\n\nThe client may:\n\n- Set a network fee that is too low, causing the transaction to fail because miners do not include it in a block (relevant for Ethereum).\n    \n- Send a transaction to the network and then cancel it immediately (BTC, ETH). Some wallets, such as Trezor, allow this.\n    \n- Send a transaction with a low network fee, cancel it, and then resend it to the same deposit address with a higher fee. In this case, we will mark the first transaction as failed and create a new one, processing the payment as a multi-deposit.\n    \n\n---\n\nBefore using the **confirming** status to credit user balances, please contact our team for additional information.\n\n# Payments statuses - (Custody flow)\n\nFor the detailed description of the statuses, please refer to the [GET \"Get custody payment status”](https://documenter.getpostman.com/view/17469357/UVyvwv7a#b62b5946-51fe-4d79-b818-4ff427dcf727) method.\n\n#### Deposit statuses\n\nIn progress:\n\n- new\n    \n- pending\n    \n- processing\n    \n- hold\n    \n\nFailed or unsuccessful (suspend the transaction on your end):\n\n- failed\n    \n- expired\n    \n\nSuccess (complete the withdrawal on your end by updating the balances, etc.):\n\n- finished\n    \n\n#### **Withdrawal statuses**\n\nIn progress:\n\n- pending\n    \n- processing\n    \n- hold\n    \n\nFailed or unsuccessful (suspend the transaction on your end):\n\n- failed\n    \n- refunded\n    \n\nSuccess (complete the withdrawal on your end by updating the balances, etc.):\n\n- finished\n    \n\n# Multiple deposit processing\n\nIn case a customer deposits to the same payAddress twice or more, it will still be processed.\n\nEach customer’s transfer is a new payment. As soon as each payment has to have its own unique externalOrderId, we add a new field \"sequence\" to differentiate the subsequent payments.\n\nThe initial payment receives an original externalOrderId and the \"sequence\" field remains unchanged.\n\nFor the subsequent payments, \"sequence\" field gets a value.\n\nExample for the first payment:\n\n- externalOrderID: 1254435345456456\n    \n- sequence: original\n    \n\nExample for the second and further payments:\n\n- externalOrderID: 1254435345456456\n    \n- sequence: 4fb7defa35a056ecc64fc74ea97ef90f (new unique ID)\n    \n\nA customer may save the deposit address on their side and send the funds without going through the process all over again.\n\nIn cases of multiple deposits, callbacks are sent to the callbackUrl of the initial payment. Our support may also notify you in case of multiple deposits.\n\nDeposit status:\n\nA new generated deposit-transaction remains in the status “waiting” for 4 hours (TTL) after the creation. If the transaction is not completed within this period, it receives status “expired”. If there is no new empty “waiting” transaction, we will generate a multi-deposit for an incoming payment.\n\nAction required: adjust your system to field sequence to handle the callbacks of multiple deposits.\n\n# Automated mistaken deposits processing\n\n**Flow description:**\n\n1\\. A user creates a deposit request in USDT (TRC20) (**payCurrency**: **usdttrc20**), but sends TRX instead (**payCurrency**: **trx**).\n\n2\\. We will automatically detect the incorrect currency and credit the funds to your balance.\n\nSince we process the deposit for the user in priceCurrency, we will send a callback to the same URL with the same **externalOrderId** and the field **sequence**: **original**, but with the updated **payCurrency**: **trx**.\n\n## Original deposit:\n\npayCurrency: **usdttrc20**  \nexternalOrderId: **order_id123**  \nsequence: **original**  \ncallbackUrl: **webhook**.url/**merchant123**\n\n## Automatically processed incorrect deposit:\n\npayCurrency: **trx**  \nexternalOrderId: **order_id123**  \nsequence: **original**  \ncallbackUrl: **webhook.url/merchant123**\n\n**Important notes:**\n\n1. The **payCurrency** of the newly processed incorrect deposit contains the actual deposited currency (as shown above).\n    \n2. The **externalOrderId** of the processed deposit is taken from the original payment.\n    \n3. We support processing incorrect deposits only within the same blockchain network.\n    \n4. The feature does not work on the Solana blockchain.\n    \n\n# Static deposit address per each customer\n\nMethods to generate a deposit transaction:\n\n**V2 flow**: `v2/payments`, `v2/payments-address`\n\n**Custody flow**: `custody/deposit`\n\nWe generate a deposit address for each unique customerId. The next time you create a deposit, your customer will receive the same deposit address.\n\nIf a customer sends a deposit without creating an order on your side, it will be considered as a [multiple deposit](https://documenter.getpostman.com/view/17469357/UVyvwv7a#multiple-deposit-processing)**.**\n\n# API errors code description\n\nV2:\n\n[https://docs.google.com/spreadsheets/d/16R_DIBU_3TIwKG7j6e2Iq6smyWXvSK5kWRYPpyPwym4/edit?gid=0#gid=0](https://docs.google.com/spreadsheets/d/16R_DIBU_3TIwKG7j6e2Iq6smyWXvSK5kWRYPpyPwym4/edit?gid=0#gid=0)\n\nCustody:\n\n[https://docs.google.com/spreadsheets/d/16R_DIBU_3TIwKG7j6e2Iq6smyWXvSK5kWRYPpyPwym4/edit?gid=1669444698#gid=1669444698](https://docs.google.com/spreadsheets/d/16R_DIBU_3TIwKG7j6e2Iq6smyWXvSK5kWRYPpyPwym4/edit?gid=1669444698#gid=1669444698)","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","isPublicCollection":false,"owner":"17469357","team":2484782,"collectionId":"314c91b8-624c-4189-a3bb-7bd5331329a0","publishedId":"UVyvwv7a","public":true,"publicUrl":"https://documenter-api.postman.tech/view/17469357/UVyvwv7a","privateUrl":"https://go.postman.co/documentation/17469357-314c91b8-624c-4189-a3bb-7bd5331329a0","customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"EF5B25"},"documentationLayout":"classic-double-column","customisation":null,"version":"8.10.1","publishDate":"2022-04-07T15:34:54.000Z","activeVersionTag":"latest","documentationTheme":"light","metaTags":{},"logos":{}},"statusCode":200},"environments":[],"user":{"authenticated":false,"permissions":{"publish":false}},"run":{"button":{"js":"https://run.pstmn.io/button.js","css":"https://run.pstmn.io/button.css"}},"web":"https://www.getpostman.com/","team":{"logo":"https://res.cloudinary.com/postman/image/upload/t_team_logo_pubdoc/v1/team/26e3ab4b3ef99913ef179f2e19fe479e16ccd1da261f5f5d1f75fccf11f1d07e","favicon":""},"isEnvFetchError":false,"languages":"[{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"HttpClient\"},{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"RestSharp\"},{\"key\":\"curl\",\"label\":\"cURL\",\"variant\":\"cURL\"},{\"key\":\"dart\",\"label\":\"Dart\",\"variant\":\"http\"},{\"key\":\"go\",\"label\":\"Go\",\"variant\":\"Native\"},{\"key\":\"http\",\"label\":\"HTTP\",\"variant\":\"HTTP\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"OkHttp\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"Unirest\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"Fetch\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"jQuery\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"XHR\"},{\"key\":\"c\",\"label\":\"C\",\"variant\":\"libcurl\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Axios\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Native\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Request\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Unirest\"},{\"key\":\"objective-c\",\"label\":\"Objective-C\",\"variant\":\"NSURLSession\"},{\"key\":\"ocaml\",\"label\":\"OCaml\",\"variant\":\"Cohttp\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"cURL\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"Guzzle\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"HTTP_Request2\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"pecl_http\"},{\"key\":\"powershell\",\"label\":\"PowerShell\",\"variant\":\"RestMethod\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"http.client\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"Requests\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"httr\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"RCurl\"},{\"key\":\"ruby\",\"label\":\"Ruby\",\"variant\":\"Net::HTTP\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"Httpie\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"wget\"},{\"key\":\"swift\",\"label\":\"Swift\",\"variant\":\"URLSession\"}]","languageSettings":[{"key":"csharp","label":"C#","variant":"HttpClient"},{"key":"csharp","label":"C#","variant":"RestSharp"},{"key":"curl","label":"cURL","variant":"cURL"},{"key":"dart","label":"Dart","variant":"http"},{"key":"go","label":"Go","variant":"Native"},{"key":"http","label":"HTTP","variant":"HTTP"},{"key":"java","label":"Java","variant":"OkHttp"},{"key":"java","label":"Java","variant":"Unirest"},{"key":"javascript","label":"JavaScript","variant":"Fetch"},{"key":"javascript","label":"JavaScript","variant":"jQuery"},{"key":"javascript","label":"JavaScript","variant":"XHR"},{"key":"c","label":"C","variant":"libcurl"},{"key":"nodejs","label":"NodeJs","variant":"Axios"},{"key":"nodejs","label":"NodeJs","variant":"Native"},{"key":"nodejs","label":"NodeJs","variant":"Request"},{"key":"nodejs","label":"NodeJs","variant":"Unirest"},{"key":"objective-c","label":"Objective-C","variant":"NSURLSession"},{"key":"ocaml","label":"OCaml","variant":"Cohttp"},{"key":"php","label":"PHP","variant":"cURL"},{"key":"php","label":"PHP","variant":"Guzzle"},{"key":"php","label":"PHP","variant":"HTTP_Request2"},{"key":"php","label":"PHP","variant":"pecl_http"},{"key":"powershell","label":"PowerShell","variant":"RestMethod"},{"key":"python","label":"Python","variant":"http.client"},{"key":"python","label":"Python","variant":"Requests"},{"key":"r","label":"R","variant":"httr"},{"key":"r","label":"R","variant":"RCurl"},{"key":"ruby","label":"Ruby","variant":"Net::HTTP"},{"key":"shell","label":"Shell","variant":"Httpie"},{"key":"shell","label":"Shell","variant":"wget"},{"key":"swift","label":"Swift","variant":"URLSession"}],"languageOptions":[{"label":"C# - HttpClient","value":"csharp - HttpClient - C#"},{"label":"C# - RestSharp","value":"csharp - RestSharp - C#"},{"label":"cURL - cURL","value":"curl - cURL - cURL"},{"label":"Dart - http","value":"dart - http - Dart"},{"label":"Go - Native","value":"go - Native - Go"},{"label":"HTTP - HTTP","value":"http - HTTP - HTTP"},{"label":"Java - OkHttp","value":"java - OkHttp - Java"},{"label":"Java - Unirest","value":"java - Unirest - Java"},{"label":"JavaScript - Fetch","value":"javascript - Fetch - JavaScript"},{"label":"JavaScript - jQuery","value":"javascript - jQuery - JavaScript"},{"label":"JavaScript - XHR","value":"javascript - XHR - JavaScript"},{"label":"C - libcurl","value":"c - libcurl - C"},{"label":"NodeJs - Axios","value":"nodejs - Axios - NodeJs"},{"label":"NodeJs - Native","value":"nodejs - Native - NodeJs"},{"label":"NodeJs - Request","value":"nodejs - Request - NodeJs"},{"label":"NodeJs - Unirest","value":"nodejs - Unirest - NodeJs"},{"label":"Objective-C - NSURLSession","value":"objective-c - NSURLSession - Objective-C"},{"label":"OCaml - Cohttp","value":"ocaml - Cohttp - OCaml"},{"label":"PHP - cURL","value":"php - cURL - PHP"},{"label":"PHP - Guzzle","value":"php - Guzzle - PHP"},{"label":"PHP - HTTP_Request2","value":"php - HTTP_Request2 - PHP"},{"label":"PHP - pecl_http","value":"php - pecl_http - PHP"},{"label":"PowerShell - RestMethod","value":"powershell - RestMethod - PowerShell"},{"label":"Python - http.client","value":"python - http.client - Python"},{"label":"Python - Requests","value":"python - Requests - Python"},{"label":"R - httr","value":"r - httr - R"},{"label":"R - RCurl","value":"r - RCurl - R"},{"label":"Ruby - Net::HTTP","value":"ruby - Net::HTTP - Ruby"},{"label":"Shell - Httpie","value":"shell - Httpie - Shell"},{"label":"Shell - wget","value":"shell - wget - Shell"},{"label":"Swift - URLSession","value":"swift - URLSession - Swift"}],"layoutOptions":[{"value":"classic-single-column","label":"Single Column"},{"value":"classic-double-column","label":"Double Column"}],"versionOptions":[],"environmentOptions":[{"value":"0","label":"No Environment"}],"canonicalUrl":"https://documenter.gw.postman.com/view/metadata/UVyvwv7a"}