{"templateId":"markdown","versions":[{"version":"v0-beta","label":"Beta (v0)","link":"/products/native-api/docs/transaction-lifecycle/webhooks","default":true,"active":true,"folderId":"e4cb5a06"}],"sharedDataIds":{"sidebar":"sidebar-products/native-api/@v0-beta/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Webhooks","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":"webhooks","__idx":0},"children":["Webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Banxa sends webhook notifications to your callback endpoint whenever a ramp's status changes or a customer's account is blocked. This eliminates the need to poll for status updates and allows you to track the complete transaction lifecycle in real time."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"setup","__idx":1},"children":["Setup"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Configure your webhook endpoint URL in the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://dashboard.banxa.com/"},"children":["Partner Dashboard"]},". You can set separate URLs for sandbox and production."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The webhook URL must:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Be publicly accessible over HTTPS."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Return a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200"]}," HTTP response to acknowledge receipt."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"available-webhooks","__idx":2},"children":["Available Webhooks"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"1-ramp-webhooks","__idx":3},"children":["1. Ramp Webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Ramp webhooks are triggered on all ramp status transitions. Banxa sends an HTTP ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST"]}," request with a JSON body."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Payload example:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"order_id\": \"fd04c5780062121628e05324003eef30\",\n  \"status\": \"FULFILLED\",\n  \"status_date\": \"2023-06-05 19:53:08\",\n  \"created_at\": \"2023-06-02 14:44:00\",\n  \"updated_at\": \"2023-06-09 13:53:08\",\n  \"internal_reason\": \"Order has been completed.\",\n  \"external_reason\": \"Your payment was successfully received. Your order is now being processed.\",\n  \"order_type\": \"ONRAMP\",\n  \"crypto_coin\": \"ETH\",\n  \"crypto_blockchain\": \"ETH\",\n  \"crypto_amount\": \"0.228632\",\n  \"fiat_currency\": \"USD\",\n  \"fiat_amount\": \"100\",\n  \"asset_price\": \"0.018656\",\n  \"payment\": \"100\",\n  \"processing_fee\": \"1.95\",\n  \"network_fee\": \"2.33\",\n  \"usd_exchange_rate\": \"1.36\",\n  \"transaction_hash\": \"0x9401a7173d7bd2ad73e8b798fdc30c83fb0529e6edbad163c549a5ad136407be\"\n}\n","lang":"json"},"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":{"align":"left","data-label":"Field"},"children":["Field"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["order_id"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["The unique Banxa identifier for the ramp."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["The new ramp status. See the status table below."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status_date"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Timestamp of the most recent status transition."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["created_at"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Timestamp of when the ramp was initially created."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["updated_at"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Timestamp of the last update to the ramp record."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["internal_reason"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Internal system description of the status — for your records."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["external_reason"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["User-facing message you can surface to your customer."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["order_type"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Transaction direction: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ONRAMP"]}," (fiat to crypto) or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["OFFRAMP"]}," (crypto to fiat)."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["crypto_coin"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Ticker symbol of the cryptocurrency (e.g., ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ETH"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["BTC"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["crypto_blockchain"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Blockchain network used for the transaction."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["crypto_amount"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Amount of cryptocurrency involved."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fiat_currency"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["ISO code of the fiat currency (e.g., ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["USD"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AUD"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fiat_amount"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Fiat amount of the transaction."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["asset_price"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Price of one unit of the cryptocurrency in the source fiat currency."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["payment"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Payment amount."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["processing_fee"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Banxa processing fee in the source fiat currency."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["network_fee"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Blockchain network (gas) fee in the source fiat currency."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["usd_exchange_rate"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Exchange rate: 1 unit of source fiat to USD at order creation time."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transaction_hash"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["On-chain transaction identifier (TXID) for the crypto transfer, if available."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Ramp status values:"]}]},{"$$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":{"align":"left","data-label":"Status"},"children":["Status"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["IN_PROGRESS"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Ramp is in progress."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAYMENT_READY"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Payment is ready to be processed."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAYMENT_ACCEPTED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Payment has been accepted."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAYMENT_RECEIVED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Payment has been received by Banxa."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAYMENT_DECLINED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Payment was declined."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAYMENT_CANCELLED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Payment was cancelled."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["COIN_DEPOSIT_READY"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Crypto deposit address is ready (off-ramp)."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["COIN_DEPOSIT_CONFIRMED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Crypto deposit has been confirmed (off-ramp)."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["COIN_TRANSFERRED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Crypto has been transferred to the customer's wallet (on-ramp)."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FIAT_TRANSFERRED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Fiat has been transferred to the customer's bank account (off-ramp)."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FULFILLED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Ramp has been completed successfully."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["REFUNDED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Ramp has been refunded."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["EXPIRED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Ramp has expired."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["EXTRA_VERIFICATION"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Customer requires additional verification before the ramp can proceed."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ACCOUNT_BLOCKED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Customer account is blocked. A separate identity webhook will also be sent."]}]}]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"2-identity-webhooks","__idx":4},"children":["2. Identity Webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Identity webhooks are triggered when a customer's account is blocked due to compliance or risk policies."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Payload example:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"identity_reference\": \"partner-customer-123\",\n  \"status\": \"ACCOUNT_BLOCKED\",\n  \"status_date\": \"2023-06-05 19:53:08\",\n  \"internal_reason\": \"Customer account is blocked.\",\n  \"external_reason\": \"Your order could not be processed.\"\n}\n","lang":"json"},"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":{"align":"left","data-label":"Field"},"children":["Field"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["identity_reference"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Your stable identifier for the customer, as provided during ramp creation."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["The identity status. Currently: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ACCOUNT_BLOCKED"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status_date"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Timestamp of the status update."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["internal_reason"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Internal system description — for your records."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["external_reason"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["User-facing message you can surface to your customer."]}]}]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"3-kyc-webhooks","__idx":5},"children":["3. KYC Webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["KYC webhooks are triggered whenever a customer's identity verification state changes. This allows you to track verification progress and prompt customers to take action when required."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Please reach out to your Banxa contact if you would like to have this webhook enabled."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Payload example:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"identityReference\": \"customer-12345\",\n  \"account\": {\n    \"exists\": true,\n    \"blocked\": false,\n    \"createdAt\": \"2023-06-05T19:53:08.320Z\"\n  },\n  \"kyc\": {\n    \"status\": \"VERIFIED\"\n  }\n}\n","lang":"json"},"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":{"align":"left","data-label":"Field"},"children":["Field"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["identityReference"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Your stable identifier for the customer, as provided during ramp creation."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["account.exists"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Whether the customer profile exists in the Banxa system."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["account.blocked"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Whether the customer account has been blocked."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["account.createdAt"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["ISO 8601 timestamp of account creation. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}," if the identity does not yet exist."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["kyc.status"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["The verification outcome of the customer's submitted identity documents (selfie + document). See status table below."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["kyc.status"]}," values:"]}]},{"$$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":{"align":"left","data-label":"Status"},"children":["Status"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PENDING"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["No identity documents have been submitted yet."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["UNDER_REVIEW"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Documents are being reviewed."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ACTION_REQUIRED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Additional action is required from the customer."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["VERIFIED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Document and liveness verification passed."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["REJECTED"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Verification was unsuccessful."]}]}]}]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Important:"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["kyc.status"]}," reflects document and liveness verification only — it does not account for Lite tier data, supplementary fields (e.g. purpose of transaction, occupation), or overall transaction eligibility. A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["VERIFIED"]}," status means the identity documents passed review; it does not mean the customer is eligible to transact. Always use the eligibility endpoint to determine whether a customer can proceed with a ramp."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"retry-behaviour","__idx":6},"children":["Retry behaviour"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If your endpoint does not respond with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]},", Banxa will automatically retry delivery with the same payload."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Retries follow a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Fibonacci sequence"]},": 1s, 2s, 3s, 5s, 8s, 13s, and so on — for a maximum of ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["2 hours"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Respond with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]}," immediately and process the event asynchronously to avoid unnecessary retries."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Implement idempotent webhook handling. In rare cases your endpoint may receive the same event more than once. Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["order_id"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status"]}," as a deduplication key."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"securing-webhooks","__idx":7},"children":["Securing Webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Banxa signs every webhook it sends using HMAC-SHA256. You verify this signature to confirm the request genuinely came from Banxa."]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Webhook verification is the reverse of request signing."]}," When you sign outbound API requests to Banxa, you use a Banxa API path in the canonical string. When you verify an incoming webhook, you use the URI path of ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["your own webhook endpoint"]}," — for example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/webhooks/banxa"]}," — not a Banxa API path. Everything else follows the same algorithm described in ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/products/native-api/docs/getting-started/authentication"},"children":["Authentication"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each webhook includes an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization"]}," header:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"Authorization: Bearer {API_KEY}:{SIGNATURE}:{NONCE}\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Banxa constructs the signature from:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"POST\\nYOUR_WEBHOOK_PATH\\nNONCE\\nPAYLOAD\n"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"verification-flow","__idx":8},"children":["Verification flow"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Extract the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization"]}," header from the incoming request"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Strip the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Bearer "]}," prefix and split on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[":"]}," to get ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["receivedKey"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["receivedSignature"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["nonce"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Recompute the expected signature using the same algorithm, your secret, and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["YOUR_WEBHOOK_PATH"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["timing-safe comparison"]}," to check that ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["receivedSignature"]}," matches — never use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["=="]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"code-examples","__idx":9},"children":["Code examples"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import hmac\n\nMY_WEBHOOK_PATH = '/webhooks/banxa'\nKEY = '[YOUR_API_KEY]'\nSECRET = '[YOUR_API_SECRET]'\n\ndef verify_webhook(auth_header, request_body):\n    _, token = auth_header.split('Bearer ', 1)\n    received_key, received_signature, nonce = token.split(':')\n\n    data = f\"POST\\n{MY_WEBHOOK_PATH}\\n{nonce}\\n{request_body}\"\n    expected = hmac.new(SECRET.encode('utf-8'), data.encode('utf-8'), 'sha256').hexdigest()\n\n    return hmac.compare_digest(received_signature, expected)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const crypto = require('crypto');\n\nconst MY_WEBHOOK_PATH = '/webhooks/banxa';\nconst KEY = '[YOUR_API_KEY]';\nconst SECRET = '[YOUR_API_SECRET]';\n\nfunction verifyWebhook(authHeader, requestBody) {\n    const token = authHeader.replace('Bearer ', '');\n    const [receivedKey, receivedSignature, nonce] = token.split(':');\n\n    const data = `POST\\n${MY_WEBHOOK_PATH}\\n${nonce}\\n${requestBody}`;\n    const expected = crypto.createHmac('sha256', SECRET).update(data).digest('hex');\n\n    return crypto.timingSafeEqual(Buffer.from(receivedSignature), Buffer.from(expected));\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"php","header":{"controls":{"copy":{}}},"source":"<?php\n$MY_WEBHOOK_PATH = '/webhooks/banxa';\n$KEY = '[YOUR_API_KEY]';\n$SECRET = '[YOUR_API_SECRET]';\n\nfunction verifyWebhook($authHeader, $requestBody, $myWebhookPath, $key, $secret) {\n    $token = str_replace('Bearer ', '', $authHeader);\n    [$receivedKey, $receivedSignature, $nonce] = explode(':', $token);\n\n    $data = implode(\"\\n\", ['POST', $myWebhookPath, $nonce, $requestBody]);\n    $expected = hash_hmac('sha256', $data, $secret);\n\n    return hash_equals($expected, $receivedSignature);\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.security.MessageDigest;\nimport java.util.Formatter;\n\nprivate static final String MY_WEBHOOK_PATH = \"/webhooks/banxa\";\nprivate static final String SECRET = \"[YOUR_API_SECRET]\";\n\npublic boolean verifyWebhook(String authHeader, String requestBody) throws Exception {\n    String token = authHeader.replace(\"Bearer \", \"\");\n    String[] parts = token.split(\":\");\n    String nonce = parts[2];\n    String receivedSignature = parts[1];\n\n    String data = \"POST\\n\" + MY_WEBHOOK_PATH + \"\\n\" + nonce + \"\\n\" + requestBody;\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())) formatter.format(\"%02x\", b);\n    String expected = formatter.toString();\n\n    return MessageDigest.isEqual(receivedSignature.getBytes(), expected.getBytes());\n}\n","lang":"java"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"swift","header":{"controls":{"copy":{}}},"source":"import CryptoKit\n\nlet MY_WEBHOOK_PATH = \"/webhooks/banxa\"\nlet SECRET = \"[YOUR_API_SECRET]\"\n\nfunc verifyWebhook(authHeader: String, requestBody: String) -> Bool {\n    let token = authHeader.replacingOccurrences(of: \"Bearer \", with: \"\")\n    let parts = token.split(separator: \":\")\n    guard parts.count == 3 else { return false }\n    let nonce = String(parts[2])\n    let receivedSignature = String(parts[1])\n\n    let data = \"POST\\n\\(MY_WEBHOOK_PATH)\\n\\(nonce)\\n\\(requestBody)\"\n    let secretKey = SymmetricKey(data: SECRET.data(using: .utf8)!)\n    let expected = HMAC<SHA256>.authenticationCode(for: data.data(using: .utf8)!, using: secretKey)\n        .map { String(format: \"%02hhx\", $0) }.joined()\n\n    return receivedSignature == expected\n}\n","lang":"swift"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ruby","header":{"controls":{"copy":{}}},"source":"require 'openssl'\n\nMY_WEBHOOK_PATH = '/webhooks/banxa'\nSECRET = '[YOUR_API_SECRET]'\n\ndef verify_webhook(auth_header, request_body)\n    token = auth_header.sub('Bearer ', '')\n    received_key, received_signature, nonce = token.split(':')\n\n    data = \"POST\\n#{MY_WEBHOOK_PATH}\\n#{nonce}\\n#{request_body}\"\n    expected = OpenSSL::HMAC.hexdigest('sha256', SECRET, data)\n\n    ActiveSupport::SecurityUtils.secure_compare(received_signature, expected)\nend\n","lang":"ruby"},"children":[]}]},"headings":[{"value":"Webhooks","id":"webhooks","depth":1},{"value":"Setup","id":"setup","depth":2},{"value":"Available Webhooks","id":"available-webhooks","depth":2},{"value":"1. Ramp Webhooks","id":"1-ramp-webhooks","depth":3},{"value":"2. Identity Webhooks","id":"2-identity-webhooks","depth":3},{"value":"3. KYC Webhooks","id":"3-kyc-webhooks","depth":3},{"value":"Retry behaviour","id":"retry-behaviour","depth":2},{"value":"Securing Webhooks","id":"securing-webhooks","depth":2},{"value":"Verification flow","id":"verification-flow","depth":3},{"value":"Code examples","id":"code-examples","depth":3}],"frontmatter":{"title":"Webhooks","description":"Banxa webhook notifications for ramp status changes and identity events. Payload reference, retry behaviour, and HMAC signature verification.","seo":{"title":"Webhooks"}},"lastModified":"2026-05-19T23:30:38.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/products/native-api/docs/transaction-lifecycle/webhooks","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}