{"templateId":"markdown","versions":[{"version":"v0-beta","label":"Beta (v0)","link":"/products/native-api/docs/getting-started/authentication","default":true,"active":true,"folderId":"e4cb5a06"}],"sharedDataIds":{"sidebar":"sidebar-products/native-api/@v0-beta/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Authentication","description":"Official Banxa API documentation – on-ramp and off-ramp transfers with identity verification and compliance.","llmstxt":{"title":"Banxa Developer Documentation","description":"Integrate crypto-fiat exchange with Banxa's licensed infrastructure: payments, KYC, compliance, and settlement handled. 150+ countries, 45 global licences.","details":{"content":"Two integration products: **Banxa Native API** for partners who manage their own KYC and want full UX control (headless, HMAC server-to-server, no Banxa-hosted screens); **Banxa Hosted Checkout** for partners who want Banxa to handle KYC and payments (three paths: Referral URL, API, or React Native SDK). Both use the same sandbox and production environments at `https://api.banxa-sandbox.com` and `https://api.banxa.com`.\n\n## Constraints\n\n- **Authentication**: HMAC credentials must be stored server-side only, never expose in frontend, mobile, or client-side code. HMAC is required for all Native API calls and for KYC sharing in Hosted Checkout; `x-api-key` is used for all other Hosted Checkout endpoints.\n- **`externalCustomerId`**: Required on every buy and sell order. Use a stable opaque identifier, never PII.\n- **`identityReference`**: Must remain constant for the same user across all requests. Must not contain PII. If you attempt to create an identity for an email that already exists, you will receive a 422 / code 81, so retrieve the existing record rather than retrying creation. Use `GET /eapi/v0/identities/{identityReference}?email=user@example.com` to look up the real `identityReference` linked to an email.\n- **`quoteId`**: Only supported by `POST /eapi/v0/ramps` (bank transfer). The React Native SDK and Embedded Payment Button do not accept a `quoteId`, so use indicative pricing (`GET /eapi/v0/price`) for SDK and Embedded Payment Button flows.\n- **Quotes**: Indicative prices are not rate-locked, so refresh close to order creation to minimise rate drift. Locked quotes (`GET /eapi/v0/quote`) expire after approximately 3 minutes and are only valid for bank transfer ramp creation.\n- **Eligibility gate**: Never create a ramp or invoke the SDK when `paymentReady` is `false`. Always check eligibility and satisfy all requirements before payment execution.\n- **Webhooks**: Verify all inbound webhook signatures with HMAC-SHA256 before processing. Return HTTP 200 immediately and process asynchronously.\n- **Product selection**: Banxa Native is for partners who manage their own KYC. Banxa Hosted Checkout is for partners who do not do KYC. These are separate products with separate flows: do not mix endpoints across products.\n- **SDK scope**: The React Native SDK has no `banxa.customerIdentity` module. Identity and KYC are handled through the Native API only. `primerCallbacks` and `primerSettings` are Native context only and must not be referenced in Hosted Checkout integrations.\n- **Payment method naming**: Never use \"eAPI\" or \"EAPI\" as a product name. The correct name is \"Banxa Native API\". The URL path prefix `/eapi/` is correct and should not be changed.\n"},"hide":false,"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"authentication","__idx":0},"children":["Authentication"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"quick-summary","__idx":1},"children":["Quick summary"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Auth type:"]}," HMAC-SHA256"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Header:"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization: Bearer API_KEY:SIGNATURE:NONCE"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Nonce:"]}," Unix timestamp in milliseconds, unique per request"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Signature:"]}," Hex-encoded HMAC-SHA256 of a newline-separated canonical string"]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"base-url","__idx":2},"children":["Base URL"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Environment"},"children":["Environment"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Base URL"},"children":["Base URL"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Sandbox"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://api.banxa-sandbox.com/eapi/v0/"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Production"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://api.banxa.com/eapi/v0/"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"authorization-header","__idx":3},"children":["Authorization header"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every request must include:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"Authorization: Bearer API_KEY:SIGNATURE:NONCE\n"},"children":[]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Component"},"children":["Component"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["API_KEY"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Public API key provided during onboarding"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SIGNATURE"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Hex-encoded HMAC-SHA256 signature"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["NONCE"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unix timestamp in milliseconds"]}]}]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"building-the-signature","__idx":4},"children":["Building the signature"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Construct a newline-separated canonical string, then sign it with HMAC-SHA256 using your API secret."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["GET request:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"METHOD\\nPATH_WITH_QUERY_STRING\\nNONCE\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["POST request:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"METHOD\\nPATH\\nNONCE\\nCOMPACT_JSON_BODY\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Rules:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use the request ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["path only"]}," — never the full URL with domain"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Include the query string in the path for GET requests"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["JSON body must be compact — no whitespace between elements"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Generate a new nonce for every request"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Examples:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"GET\\n/eapi/v0/price\\n1612391416000\n\nPOST\\n/eapi/v0/ramps\\n1612391416000\\n{\"identityReference\":\"example_01\"}\n"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"code-examples","__idx":5},"children":["Code examples"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import hmac\nimport time\n\nkey = '[YOUR_API_KEY]'\nsecret = '[YOUR_API_SECRET]'\n\ndef generate_hmac(method, path, payload=None):\n    nonce = str(int(time.time() * 1000))\n    parts = [method, path, nonce]\n    if payload:\n        parts.append(payload)\n    data = '\\n'.join(parts)\n    signature = hmac.new(secret.encode('utf-8'), data.encode('utf-8'), 'sha256').hexdigest()\n    return f'{key}:{signature}:{nonce}', nonce\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const crypto = require('crypto');\n\nconst key = '[YOUR_API_KEY]';\nconst secret = '[YOUR_API_SECRET]';\n\nfunction generateHmac(method, path, payload = null) {\n    const nonce = Date.now().toString();\n    const parts = [method, path, nonce];\n    if (payload) parts.push(payload);\n    const data = parts.join('\\n');\n    const signature = crypto.createHmac('sha256', secret).update(data).digest('hex');\n    return `${key}:${signature}:${nonce}`;\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"php","header":{"controls":{"copy":{}}},"source":"<?php\n$key = '[YOUR_API_KEY]';\n$secret = '[YOUR_API_SECRET]';\n\nfunction generateHmac($method, $path, $payload, $key, $secret) {\n    $nonce = (string)(time() * 1000);\n    $parts = [$method, $path, $nonce];\n    if ($payload) $parts[] = $payload;\n    $data = implode(\"\\n\", $parts);\n    $signature = hash_hmac('sha256', $data, $secret);\n    return \"{$key}:{$signature}:{$nonce}\";\n}\n","lang":"php"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"java","header":{"controls":{"copy":{}}},"source":"import javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.util.Formatter;\n\npublic class BanxaAuth {\n    private static final String KEY = \"[YOUR_API_KEY]\";\n    private static final String SECRET = \"[YOUR_API_SECRET]\";\n\n    public String generateHmac(String method, String path, String payload) throws Exception {\n        String nonce = String.valueOf(System.currentTimeMillis());\n        String data = method + \"\\n\" + path + \"\\n\" + nonce;\n        if (payload != null) data += \"\\n\" + payload;\n\n        SecretKeySpec signingKey = new SecretKeySpec(SECRET.getBytes(), \"HmacSHA256\");\n        Mac mac = Mac.getInstance(\"HmacSHA256\");\n        mac.init(signingKey);\n        Formatter formatter = new Formatter();\n        for (byte b : mac.doFinal(data.getBytes())) {\n            formatter.format(\"%02x\", b);\n        }\n        return KEY + \":\" + formatter.toString() + \":\" + nonce;\n    }\n}\n","lang":"java"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import CryptoKit\n\nlet key = \"[YOUR_API_KEY]\"\nlet secret = \"[YOUR_API_SECRET]\"\n\nfunc generateHmac(method: String, path: String, payload: String? = nil) -> String {\n    let nonce = String(Int(Date().timeIntervalSince1970 * 1000))\n    var parts = [method, path, nonce]\n    if let payload = payload { parts.append(payload) }\n    let data = parts.joined(separator: \"\\n\")\n    let secretKey = SymmetricKey(data: secret.data(using: .utf8)!)\n    let signature = HMAC<SHA256>.authenticationCode(for: data.data(using: .utf8)!, using: secretKey)\n        .map { String(format: \"%02hhx\", $0) }.joined()\n    return \"\\(key):\\(signature):\\(nonce)\"\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ruby","header":{"controls":{"copy":{}}},"source":"require 'openssl'\n\nKEY = '[YOUR_API_KEY]'\nSECRET = '[YOUR_API_SECRET]'\n\ndef generate_hmac(method, path, payload = nil)\n    nonce = (Time.now.to_f * 1000).to_i.to_s\n    parts = [method, path, nonce]\n    parts << payload if payload\n    data = parts.join(\"\\n\")\n    signature = OpenSSL::HMAC.hexdigest('sha256', SECRET, data)\n    \"#{KEY}:#{signature}:#{nonce}\"\nend\n","lang":"ruby"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"authentication-errors","__idx":6},"children":["Authentication errors"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Code"},"children":["Code"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Cause"},"children":["Cause"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["40001"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Nonce is not a valid Unix timestamp in milliseconds"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["40002"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Nonce is too old — check your system clock is in sync"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["40003"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Nonce already used — generate a new nonce per request"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["40100"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["API key not recognised — check you are using the correct environment key"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["40101"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Authorization header is malformed — format must be ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Bearer API_KEY:SIGNATURE:NONCE"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["40102"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Authorization header is missing"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["40103"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Signature mismatch — check path, newline separators, compact JSON, and correct secret"]}]}]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"best-practices","__idx":7},"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"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Keep your system clock in sync (NTP)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Always serialize JSON with ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["no whitespace"]}," before signing"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["request path only"]}," — never the full URL with domain"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Log the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["request_id"]}," from error responses for debugging"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If issues persist, contact your Banxa Account Manager."]}]},"headings":[{"value":"Authentication","id":"authentication","depth":1},{"value":"Quick summary","id":"quick-summary","depth":2},{"value":"Base URL","id":"base-url","depth":2},{"value":"Authorization header","id":"authorization-header","depth":2},{"value":"Building the signature","id":"building-the-signature","depth":2},{"value":"Code examples","id":"code-examples","depth":2},{"value":"Authentication errors","id":"authentication-errors","depth":2},{"value":"Best practices","id":"best-practices","depth":2}],"frontmatter":{"title":"Authentication","description":"How to authenticate with the Banxa Native API using HMAC-SHA256.","seo":{"title":"Authentication"}},"lastModified":"2026-05-19T23:30:38.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/products/native-api/docs/getting-started/authentication","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}