# Frequently Asked Questions

The questions partners ask most often during integration, with links to the relevant guides.

## Credentials and authentication

### Which API key do I use for which endpoint?

Banxa has two credential types, and using the wrong one is the most common cause of `401 Unauthorized` errors:

| Credential | Used for |
|  --- | --- |
| **v2 API key** (`x-api-key` header) | All `/v2` endpoints: quotes, orders, payment methods, fiats, crypto, countries |
| **HMAC key pair** (API key + secret) | `POST /v2/identities/token/share` (KYC sharing), and verifying incoming webhook signatures |


Both credential sets are visible in the merchant dashboard, per environment. See [Authentication & Environments](/products/hosted-checkout/docs/getting-started/authentication-and-environments).

### Which secret verifies webhook signatures?

Webhook signatures are verified with your **HMAC secret**, not your v2 `x-api-key`. If you only have a v2 key, retrieve the HMAC credentials from the merchant dashboard or contact your account manager. See [Webhooks](/products/hosted-checkout/docs/transaction-lifecycle/webhooks#securing-webhooks).

### I'm getting 401 errors even though my key is correct

Check that you are calling the right environment. Sandbox keys only work against `https://api.banxa-sandbox.com`, production keys only against `https://api.banxa.com`. The base URL for each environment is shown in the merchant dashboard next to your API key.

## Sandbox

### Why is my sandbox order stuck and never reaching `complete`?

Sandbox orders do not settle automatically in all cases and may require manual fulfilment by Banxa. No crypto moves on-chain in sandbox. If your order is stuck, contact your integration manager to progress it. See [Testing Overview](/products/hosted-checkout/docs/testing/overview#sandbox-limitations).

### Why do sandbox prices look wrong?

Sandbox pricing is not refreshed at production frequency. Use sandbox to validate the integration flow, not pricing accuracy. Production rates are live.

### Can I test KYC sharing end-to-end in sandbox?

The KYC sharing API accepts requests in sandbox, but the sandbox environment is not connected to Sumsub. Test user profiles are verified manually by Banxa; ask your integration manager to verify each test user profile you create.

### Can I use testnet wallet addresses?

No. Sandbox validates mainnet address formats only. Use a valid mainnet-format address; no on-chain transaction occurs.

## Orders and settlement

### When is an order final?

Only the terminal statuses are final: `complete`, `cancelled`, `declined`, `refunded`. In particular, `paymentReceived` is **not** final: an order can still be cancelled after payment if it is held for risk or compliance review. Do not credit your customer or trigger downstream fulfilment until you receive `complete`. See [Order Statuses](/products/hosted-checkout/docs/transaction-lifecycle/order-statuses).

### What is the difference between the order ID and the order key?

Orders have two identifiers: a short numeric ID shown to the customer in the checkout and in emails, and a long hash used in API responses and webhook payloads. Always reconcile using the hash. See [Order Lookup](/products/hosted-checkout/docs/transaction-lifecycle/order-lookup).

### Why does the merchant dashboard show a different status than my webhooks?

Webhooks are the real-time source of truth. The merchant dashboard order view can lag behind. Build your reconciliation on webhooks and the order lookup endpoint, not the dashboard.

### How long is a checkout URL valid?

The `checkoutUrl` returned by `POST /v2/buy` is valid for 1 minute, starting from Banxa's response to the order creation request. Once the URL has been loaded there is no further expiry. Create the order at the moment the customer confirms, then open the URL immediately. Do not create orders in advance.

## Pricing and fees

### Why doesn't the quote response show all fees?

The quote response shows `processingFee` and `networkFee` explicitly. The Banxa spread is embedded in the exchange rate, so for some payment methods `processingFee` can legitimately be `0` while the spread is applied via the rate. To calculate the effective all-in rate, divide the fiat amount by the crypto amount.

### Is a quote a guaranteed price?

No. Quotes are indicative and carry no quote ID; there is nothing to pass into order creation. The final price is confirmed at checkout. Re-quote whenever the customer changes amount, currency, or payment method. See [Get a Quote](/products/hosted-checkout/docs/api-integration/get-a-quote).

## Customers and KYC

### Do I need to send `externalCustomerId`?

Yes, it is required on buy and sell orders. Use one stable identifier **per customer**, not per order. Reusing the same `externalCustomerId` is what lets returning verified customers skip repeat KYC. Generating a new ID per order breaks returning-customer recognition.

### Do I call the KYC sharing endpoint per customer or per order?

Once per customer. After sharing, pass the same `externalCustomerId` on every order for that customer. Calling it again for the same customer is safe and will not error. See [KYC Sharing](/products/hosted-checkout/docs/identity-compliance/kyc-sharing).

### Why am I getting "The feature is not enabled" (code 4005) on the identity endpoint?

KYC sharing must be enabled on your account by Banxa, per environment, after a compliance review. If you see this error in sandbox or production, the feature has not yet been enabled for that environment. Contact your account manager. See [Error Codes](/products/hosted-checkout/docs/reference/error-codes).

### Why was my customer's order delayed even though payment succeeded?

The most common cause is a name mismatch: the name on the payment instrument must match the name on the KYC profile. Mismatches route the order to manual review, which delays fulfilment.

### The KYC camera step fails inside my iframe or WebView

Your embedding container must grant camera and microphone permissions. The fix depends on how you embed the checkout:

- **Web (iframe):** include `allow="camera; microphone"` on the `<iframe>` element that loads the checkout. Without it, users see a "Failed to acquire camera" error during KYC. See [iFrame Overview](/products/hosted-checkout/docs/checkout-experience/iframe/iframe-overview).
- **Mobile app (WebView):** camera permissions must be granted at the app level. On Android, declare `CAMERA` and `RECORD_AUDIO` in your manifest and grant permission requests in your `WebChromeClient`. On iOS, implement `WKUIDelegate` to grant media capture requests, and note that KYC liveness requires iOS 15+ in `WKWebView`. Full configuration examples: [Embedded Checkout Mobile](/products/hosted-checkout/docs/checkout-experience/iframe/webview-mobile).


If the desktop-to-phone QR handoff works but your in-app or in-page flow fails, missing container permissions are almost always the cause: the QR path opens the KYC step directly in the phone browser, outside your iframe or WebView.

## Webhooks

### How many webhook URLs can I configure?

One webhook URL per environment (one for sandbox, one for production), configured in the merchant dashboard.

### Are order webhooks and KYC webhooks the same?

They share the same configured URL but are separate notification types and may originate from different Banxa systems. Handle them independently by inspecting the payload. See [Webhooks](/products/hosted-checkout/docs/transaction-lifecycle/webhooks).

### I received `paymentReceived` but never a final webhook

Final settlement can take from minutes to hours depending on payment method, blockchain confirmation times, and whether the order is held for review. If an order remains non-terminal for an extended period, check it via [Order Lookup](/products/hosted-checkout/docs/transaction-lifecycle/order-lookup) before escalating.

Still stuck? Contact your integration manager, or see the [Error Codes](/products/hosted-checkout/docs/reference/error-codes) reference.