{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-products/legacy-api/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Authorization Header","description":"Official Banxa API documentation – on-ramp and off-ramp transfers with identity verification and compliance.","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Banxa API implements an ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://en.wikipedia.org/wiki/HMAC"},"children":["HMAC"]}," authentication strategy which requires the payload of the message to be hashed. The hashing of the payload needs to be correct, otherwise, the API request will be rejected."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"authorization-header","__idx":0},"children":["Authorization Header"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For all requests to the Banxa API, add an Authorization digest to the header. The header structure must be in the following format:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"Authorization: Bearer {API Key}:{Signature}:{Nonce}\n"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"api-key","__idx":1},"children":["API Key"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The API public key credential. This is the one provided by us during on-boarding."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"signature","__idx":2},"children":["Signature"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Message authentication signature. The request message is hashed with the API Secret using SHA256 algorithm.",{"$$mdtype":"Tag","name":"br","attributes":{},"children":[]},"The structure of the message:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Request method. (e.g. \"GET or \"POST\")"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Request URI path including the query parameters for GET."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["nonce"]}," value (e.g. using unix timestamp value)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The payload in JSON format only for POST, with no whitespace."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each of the items in the message should be separated by a new line for example"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"'GET' + '\\n' + '/api/coins' + '\\n' + '1612391416'\n"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"'POST' + '\\n' + '/api/orders' + '\\n' + '1612391416' + '\\n' + '{\"account_reference\":\"example_01\"}'\n"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"nonce","__idx":3},"children":["Nonce"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A unix timestamp in milliseconds generated for this request."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"sample-code","__idx":4},"children":["Sample Code"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Here is some sample code around how to call the API including the HMAC authentication. Key things to note when building your integration:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The signature passed to the HMAC algorithm are separated by new lines (see above section)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The json payload for POST requests should contain no whitespace"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The nonce should be a unix timestamp which is generated for every request"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import requests\nimport time\nimport hmac\n\nurl = 'https://[PARTNER-NAME].banxa-sandbox.com'\nkey = '[YOUR_MERCHANT_KEY]'\nsecret = '[YOUR_MERCHANT_SECRET]'\n\ndef generateHmac(payload, nonce):\n    hmacCode = hmac.new(secret.encode('utf-8'), payload.encode('utf-8'), 'sha256').hexdigest()\n    return key + ':' + hmacCode + ':' + str(nonce)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"java","header":{"controls":{"copy":{}}},"source":"package com.banxa;\n\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\nimport java.time.Duration;\nimport java.util.Formatter;\n\npublic class BanxaService {\n    private static final String BANXA_URL = \"https://api.banxa-sandbox.com\";\n    private static final String KEY = \"[YOUR_MERCHANT_KEY]\";\n    private static final String SECRET = \"[YOUR_MERCHANT_SECRET]\";\n\n    public String getHmac(String method, String query, String payload) throws Exception {\n        String nonce = String.valueOf(System.currentTimeMillis());\n\n        String data = method + \"\\n\" +\n                query + \"\\n\" +\n                nonce;\n\n        if (payload != null) {\n            data += \"\\n\" + payload;\n        }\n\n        SecretKeySpec signingKey = new SecretKeySpec(SECRET.getBytes(), \"HmacSHA256\");\n        Mac mac = Mac.getInstance(\"HmacSHA256\");\n        mac.init(signingKey);\n        return KEY + \":\" + toHexString(mac.doFinal(data.getBytes())) + \":\" + nonce;\n    }\n\n    private String toHexString(byte[] bytes) {\n        Formatter formatter = new Formatter();\n        for (byte b : bytes) {\n            formatter.format(\"%02x\", b);\n        }\n        return formatter.toString();\n    }\n}\n","lang":"java"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"php","header":{"controls":{"copy":{}}},"source":"<?php\n  $url = \"https://api.banxa-sandbox.com\";\n  $key = '[YOUR_MERCHANT_KEY]';\n  $secret = '[YOUR_MERCHANT_SECRET]';\n\n  function generateHmac($payload, $nonce, $key, $secret) {\n    $sign = hash_hmac('sha256', $payload, $secret);\n    return $key . ':' . $sign . ':' . $nonce;\n  }\n\n  $nonce = time() * 1000;\n","lang":"php"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"function generateHmac(method, path, nonce, payload) {\n    const crypto = require(\"crypto\");\n    const key = \"[YOUR_MERCHANT_KEY]\";\n    const secret = \"[YOUR_MERCHANT_SECRET]\";\n\n    const parts = [method, path, String(nonce)];\n    if (payload) parts.push(payload);\n    const dataToSign = parts.join(\"\\n\");\n\n    const localSignature = crypto\n      .createHmac(\"sha256\", secret)\n      .update(dataToSign)\n      .digest(\"hex\");\n\n    return `${key}:${localSignature}:${nonce}`;\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import CryptoKit\n\nlet key = \"[YOUR_MERCHANT_KEY]\"\nlet secret = \"[YOUR_MERCHANT_SECRET]\".data(using: .utf8)!\nlet secretKey = SymmetricKey(data: secret)\n\npublic func generateHmac(\n    method: String, \n    query: String, \n    data: Optional<String> = nil\n    ) -> String {\n    \n    let nonce = String(Int(Date().timeIntervalSince1970 * 1000))\n    var payload = method + \"\\n\" + query + \"\\n\" + nonce\n    \n    if((data) != nil) {\n        payload = payload + \"\\n\" + (data!)\n    }\n\n  \tlet payloadData = payload.data(using: .utf8)!\n    let hmac = HMAC<SHA256>.authenticationCode(for: payloadData, using: secretKey)\n    let hmacByteString = hmac.map { String(format: \"%02hhx\", $0) }.joined()\n    \n    return key + \":\" + hmacByteString + \":\" + nonce\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ruby","header":{"controls":{"copy":{}}},"source":"require 'openssl'\n\ndef generate_hmac(time, query, body = nil, method = 'POST')\n  secret = API_SECRET\n  parts = [method, query.to_s, time.to_s]\n  parts << body if body\n  data = parts.join(\"\\n\")\n  hmac = OpenSSL::HMAC.hexdigest('sha256', secret, data)\n  \"#{API_KEY}:#{hmac}:#{time.to_s}\"\nend\n","lang":"ruby"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"authentication-error-codes","__idx":5},"children":["Authentication Error Codes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you receive a 401 HTTP error when sending a request to the Banxa API, typically there is something wrong with your HMAC header. Here are some common reasons for certain error codes."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"error-code-starting-with-400","__idx":6},"children":["Error code starting with 400"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Code ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["40001"]}," - this is normally when you are sending us a nonce that is not a valid unix timestamp in milliseconds. Please check your nonce is a 13-digit integer with no other characters."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Code ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["40002"]}," - this is normally if the nonce was generated too long before the request was sent or your system clock is not up to date."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Code ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["40003"]}," - we have seen this nonce before. Ensure you generate a new nonce for every request. To reduce the number of conflicts we do only check this one for POST requests and it used to avoid replay attacks."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"error-code-starting-with-401","__idx":7},"children":["Error code starting with 401"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Code ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["40100"]}," - Check the API Key you have sent is for the correct environment and matches what we sent you"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Code ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["40101"]}," - The Authorization header is malformed. Check again the format given above"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Code ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["40102"]}," - The Authorization header is missing."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Code ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["40103"]}," - We were unable to generate the same signature to match the one sent on the Banxa side. Please ensure that you have followed the instructions to build the payload such as each piece of data is separated by a new line, the JSON payload has no whitespace between the elements and also that you are passing the secret and payload into the correct fields of your HMAC algorithm"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Code ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["40104"]}," - Check the API Key you have sent is for the correct environment and matches what we sent you"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you cannot solve your issues with this help, then feel free to contact your Account Manager for support."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"best-practices","__idx":8},"children":["Best Practices"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Generate a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["new nonce for every request"]}," — reuse will be rejected"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Keep your system clock in sync (NTP) to avoid nonce expiry errors"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Serialize request bodies with ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["no whitespace"]}," before signing"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Sign the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["request path only"]}," — never the full URL including domain"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Store your API secret in a secure secret store, never in source code or client-side storage"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Rotate your secret immediately if you suspect it has been exposed"]}]}]},"headings":[{"value":"Authorization Header","id":"authorization-header","depth":2},{"value":"API Key","id":"api-key","depth":3},{"value":"Signature","id":"signature","depth":3},{"value":"Nonce","id":"nonce","depth":3},{"value":"Sample Code","id":"sample-code","depth":2},{"value":"Authentication Error Codes","id":"authentication-error-codes","depth":2},{"value":"Error code starting with 400","id":"error-code-starting-with-400","depth":3},{"value":"Error code starting with 401","id":"error-code-starting-with-401","depth":3},{"value":"Best Practices","id":"best-practices","depth":2}],"frontmatter":{"title":"Step 3: Authentication","seo":{"title":"Authorization Header"}},"lastModified":"2026-05-19T23:30:38.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/products/legacy-api/docs/on-ramp-off-ramp/on-ramp-api-tutorial/step-3-authentication","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}