For the complete documentation index, see llms.txt. Append
.mdto any page URL for its markdown version.
Standalone KYC lets Banxa verify a customer's identity before they place an order. The customer completes verification in Banxa-hosted identity screens, which you show in an iframe or webview, or by redirecting the customer. Once the customer is verified, eligibility returns paymentReady: true and you take payment with Banxa Native, in your own UI.
Use Standalone KYC if you don't run your own KYC and don't want to build document, selfie, and liveness capture yourself.
In the Integration Guide's Quick start path, Banxa may route a user into a hosted WebView during checkout, after an order is created. Standalone KYC runs before any order, so the checkout itself stays fully native.
Standalone KYC enabled for your account. Contact your Banxa integration manager. Until it's enabled, session requests return
403.KYC webhooks enabled. The KYC webhook is how you learn the verification result. Ask your Banxa integration manager to confirm it's on for your account.
How you'll show the flow. Tell Banxa whether you'll use an iframe, a webview, or a redirect, so your account is set up to send the right completion signal.
Two sets of API credentials. Standalone KYC uses two Banxa APIs, so you need both:
- your Native API HMAC key and secret, for eligibility and ramps
- a Hosted Checkout API key, sent in the
x-api-keyheader, for the session endpointPOST /{partnerRef}/v2/kyc/sessions. It's the same key and partner reference you initialise the Banxa payment SDKs with.
If you don't have a Hosted Checkout API key, contact your Banxa integration manager.
- Your backend creates a session, passing the user's
identityReferenceasexternalCustomerId. - Banxa returns a link and its expiry. The link is valid for 1 hour.
- You show the link to the customer.
- The customer enters their email and country (and an email one-time code, if that's enabled for your account). They then complete only the identity steps their verification level needs and that they haven't already completed.
- When nothing is left to collect, the customer sees "Your account is under review".
- Your app receives a completion signal. This means the customer finished, not that they passed.
- Banxa sends a KYC webhook when the result is known.
- Check eligibility. If
paymentReadyistrue, continue with your payment method guide.
externalCustomerId and identityReference are the same identifier. Pass the user's identityReference as externalCustomerId, so the verification attaches to the identity you check eligibility against. The KYC webhook returns the same value as identity_reference.
If no identity exists yet for that identityReference, creating the session creates one.
Banxa matches the customer by the email they enter in the hosted screens. If that email belongs to a different Banxa customer than the one your identityReference currently points to, your identityReference moves to that customer. Eligibility then reflects that customer, not the one you created earlier.
Make sure the customer uses the same email you used when you created the identity. If a customer you know is verified comes back as paymentReady: false, check whether they used a different email.
This endpoint uses your Hosted Checkout API key, not HMAC. Create sessions from your backend, so the key never ships in your app or web page.
POST /{partnerRef}/v2/kyc/sessions
x-api-key: YOUR_HOSTED_CHECKOUT_API_KEY
Content-Type: application/json
{
"externalCustomerId": "partner-abc123",
"country": "AU",
"tier": "standard",
"returnUrl": "https://yourapp.com/kyc/done"
}| Field | Required | Description |
|---|---|---|
externalCustomerId | Yes | Your identityReference for the user. Up to 255 characters. Longer values are rejected, not truncated. |
country | No | The customer's country of residence, as a 2-letter ISO 3166-1 code. The hosted screens still ask the customer for their country. Banxa saves a country only if the customer doesn't already have one, so whichever arrives first, yours or the customer's, is the one that's kept. |
tier | No | standard (default), enhanced, or express. See Verification levels. |
returnUrl | No | An https URL or an app deep link, for example myapp://kyc-done. Where the customer goes when they finish. See Completion signals. If you leave it out, the default return URL set for your account in the Merchant Dashboard is used, if there is one. |
Response 201
{
"redirectUrl": "https://...",
"expiresAt": "2026-08-21T08:10:00+00:00"
}Repeat calls are safe. If you call again for the same externalCustomerId while the link is still valid, you get the same link and expiry back. A new tier takes effect the next time the customer loads a screen. A new country is only used if the customer doesn't have one yet. A new returnUrl only takes effect once the current link expires and you create a new one.
Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid Hosted Checkout API key, or the key doesn't belong to {partnerRef}. |
403 | Standalone KYC isn't enabled for your account. |
422 | A field failed validation, for example an unsupported tier. |
502 | Temporary upstream error. Retry with backoff. |
See the API reference for the full schema.
You request a level with tier. Banxa combines it with the customer's country to decide exactly what to collect. Banxa's compliance team sets these requirements, and they can change.
standard(default): personal details, an ID document, and a selfie with a liveness check, plus any region-specific items such as a tax ID, purpose of transaction, or occupation.enhanced: everything instandard, plus further items such as source of funds or proof of address, depending on the region.express: personal details only, plus a tax ID in some regions. No ID document or selfie. Express must be enabled by Banxa for your account and for each country, and it isn't available in every country. If express isn't available, the customer getsstandardand you don't receive an error. Banxa always falls back to more verification, never less.
If the customer's country isn't covered by a regional level, they get a catch-all level that asks for more than most regions. Customers in some regions may also be asked to complete a cooling-off period before their first order.
Whichever level you request, always rely on eligibility to decide whether a specific transaction can proceed.
| How you show it | What you do | Completion signal |
|---|---|---|
| iframe | Embed redirectUrl in an iframe with allow="camera; microphone". | A postMessage to your page |
| Webview | Load redirectUrl in a webview with camera and microphone access. | Redirect to returnUrl |
| Redirect | Send the browser to redirectUrl. | Redirect to returnUrl |
Banxa doesn't close the iframe or webview. Close it yourself after the completion signal.
The Banxa SDKs handle the webview, camera and microphone setup, and the completion signal for you.
iframe: Banxa posts these messages to your page with postMessage. These are the only messages the iframe sends.
type | Payload | When |
|---|---|---|
banxa:loading | { "type": "banxa:loading", "status": "loading" } | The flow is loading |
banxa:error | { "type": "banxa:error", "code": "...", "message": "..." } | Something went wrong. code and message describe the error |
banxa:kyc-complete | { "type": "banxa:kyc-complete", "status": "complete" } | The customer reached the end of the flow |
The banxa:kyc-complete payload is always the same. It never says whether the customer passed. Only accept messages whose event.origin matches the origin of redirectUrl. Banxa needs to enable embedded mode for your account and register your page's domain. Without embedded mode, the iframe sends no messages at all.
The Banxa SDKs report a few more events of their own, such as KYC errors and rejected messages. See your SDK reference.
Webview or redirect: when the customer finishes, they see the "Your account is under review" screen. If auto-redirect is enabled for your account, they're sent to returnUrl straight away. Otherwise, the screen shows a "Return to [your name]" button that takes them there. If you didn't send a returnUrl, your account's default return URL is used instead. With neither set, the customer stays on the under-review screen.
The "under review" screen and the completion signal don't tell you the outcome. Use:
- The KYC webhook. Banxa sends it when the customer's verification status changes. It identifies the customer with
identity_referenceand carrieskyc.status. See KYC webhooks. - Eligibility. Before creating a ramp, call
POST /eapi/v0/identities/transactions/eligibilitywith the sameidentityReference. Proceed only whenpaymentReadyistrue.
At the moment there's no endpoint to query a Standalone KYC result, and no session status endpoint. Don't call GET /eapi/v0/identities/{identityReference} to get the status of a Standalone KYC verification. Use the KYC webhook and eligibility only.
The Banxa SDKs can show a Standalone KYC session and report when the customer finishes. Create the session on your backend and pass redirectUrl to your app, then use your SDK's KYC view:
The SDKs don't poll for a result. Their completion callback means the customer finished, not that they passed. Wait for the KYC webhook or check eligibility before creating an order.
- Use the sandbox base URL:
https://api.banxa-sandbox.com/{partnerRef}/v2/kyc/sessions. In production, usehttps://api.banxa.com. - Use a new email address for each test customer. If you reuse an email, Banxa treats the session as that existing customer.
- Test the document and selfie steps on a real device. Simulators don't provide a real camera.
- If you use an iframe, check that you receive
banxa:kyc-completeexactly once.
| Symptom | Likely cause | What to do |
|---|---|---|
401 when creating a session | Missing or wrong x-api-key, or {partnerRef} doesn't match the account the key belongs to | Use your Hosted Checkout API key (not your Native API HMAC credentials) and the matching partner reference |
403 when creating a session | Standalone KYC isn't enabled for your account | Ask your Banxa integration manager to enable it |
422 when creating a session | A field failed validation, for example an unsupported tier, or an externalCustomerId longer than 255 characters | Fix the field named in the error response |
| The link has expired | Links last 1 hour | Create a session again for the same externalCustomerId to get a new link. The customer isn't asked again for anything they've already completed |
| The camera doesn't work during the document or selfie step | The iframe or webview has no camera or microphone access | iframe: add allow="camera; microphone". Webview or app: grant camera and microphone permissions. See your SDK reference |
| Nothing happens in your app when the customer finishes | The completion signal isn't set up for how you show the flow | iframe: ask Banxa to enable embedded mode for your domain. Webview or redirect: send a returnUrl, and ask Banxa to enable auto-redirect so the customer doesn't have to tap Return |
| No KYC webhook arrives | KYC webhooks or your webhook URL aren't set up for your account | Ask your Banxa integration manager to enable KYC webhooks and confirm your webhook URL |
Eligibility returns paymentReady: false right after the customer finished | Completion only means the customer finished, not that they passed | Wait for the KYC webhook, then check eligibility again |
You requested express, but the customer got the full document and selfie check | Express isn't enabled for your account in the customer's country, or it isn't offered in that country | Ask your Banxa integration manager whether express can be enabled for that country. Otherwise this is expected: Banxa falls back to standard |