Web SDK
The Kora IDV Web SDK ships as two npm packages:
@koraidv/core— framework-agnostic. API client, verification engine, capture pipeline, types. Use this directly from vanilla JS, Vue, Svelte, Solid, etc.@koraidv/react— React components and hooks built on top of@koraidv/core. Drop-in screens (<VerificationFlow />,<DocumentCaptureScreen />,<LivenessScreen />) plus auseKoraIDVhook.
Both run entirely in the browser — camera capture via getUserMedia, document detection and liveness inference via in-browser ML models. No additional server install required.
Requirements
- Modern browser with
getUserMediaandWebAssemblysupport (Chrome 90+, Safari 16+, Firefox 90+, Edge 90+) - Site served over HTTPS (camera APIs are gated to secure contexts)
- Bundler that supports ES modules (Vite, Webpack 5, esbuild, Rollup, etc.)
Installation
React
npm install @koraidv/react @koraidv/core react react-dom
@koraidv/core is a peer of @koraidv/react, so install both. react and react-dom are also peers (>= 17).
Vanilla / non-React
npm install @koraidv/core
Quick start (React)
import { KoraIDVProvider, VerificationFlow } from '@koraidv/react';
function App() {
return (
<KoraIDVProvider
apiKey="kora_sandbox_xxxxx"
tenantId="your-tenant-uuid"
config={{ environment: 'sandbox' }}
>
<VerificationFlow
externalId={`user-${userId}`}
tier="standard"
onComplete={(v) => console.log('done', v.riskScore)}
onError={(e) => console.error(e.code)}
onCancel={() => console.log('cancelled')}
/>
</KoraIDVProvider>
);
}
The provider takes apiKey and tenantId directly; everything else (environment, theme, livenessMode) goes inside the config prop.
<VerificationFlow> renders the full multi-step UI: country selection → document type → document capture → selfie → liveness → result. The onComplete callback fires once the verification reaches a terminal state.
Quick start (vanilla JS)
@koraidv/core exposes a granular pipeline API rather than a single mounted-component flow. You drive the steps yourself or render UI with @koraidv/react's individual screen components. The minimal callback-driven flow:
import { KoraIDV } from '@koraidv/core';
const kora = new KoraIDV({
apiKey: 'kora_sandbox_xxxxx',
tenantId: 'your-tenant-uuid',
environment: 'sandbox',
});
await kora.startVerification(
{
externalId: `user-${userId}`,
tier: 'standard',
},
{
onComplete: (verification) => {
console.log('Risk score:', verification.riskScore);
},
onStepChange: (step) => {
console.log('Now on step:', step);
},
onError: (err) => console.error(err.code, err.message),
onCancel: () => console.log('cancelled'),
}
);
After startVerification resolves, call kora.uploadDocument(blob, 'front', docType), kora.uploadSelfie(blob), kora.startLivenessSession() / kora.submitLivenessChallenge(...), and finally kora.completeVerification(). Most consumers reach for @koraidv/react's flow components instead — they wrap these calls behind a state machine.
Composing your own UI
If you want full control over layout and styling, import the individual screen components and build your own state machine. They all live under @koraidv/react:
import {
CountrySelectionScreen,
DocumentSelectionScreen,
DocumentCaptureScreen,
SelfieCaptureScreen,
LivenessScreen,
ResultScreen,
StepProgressBar,
} from '@koraidv/react';
Each accepts its own props (selected country, document type, image data, callbacks). See the package source for full prop types.
Configuration
type Configuration = {
apiKey: string;
tenantId: string;
environment?: 'sandbox' | 'production'; // auto-detected from key prefix
baseUrl?: string; // override for on-prem
livenessMode?: 'active' | 'passive';
theme?: {
primaryColor?: string;
backgroundColor?: string;
textColor?: string;
cornerRadius?: number;
};
timeout?: number; // ms, default 120000
};
For React, apiKey and tenantId go on the provider directly; everything else nests under config:
<KoraIDVProvider
apiKey="…"
tenantId="…"
config={{
environment: 'production',
livenessMode: 'active',
theme: { primaryColor: '#0D9488' },
}}
>
<App />
</KoraIDVProvider>
Hook (React)
useKoraIDV() returns a state object plus action methods:
import { useKoraIDV } from '@koraidv/react';
function VerifyPage() {
const { state, startVerification, resumeVerification, acceptConsent } = useKoraIDV();
// state.step, state.verification, state.error, state.isLoading
return (
<div>
{state.step === 'idle' && (
<button onClick={() => startVerification(`user-${id}`, 'standard')}>
Start
</button>
)}
{state.verification && (
<div>Risk score: {state.verification.riskScore}</div>
)}
</div>
);
}
Verification tiers
| Tier | Includes |
|---|---|
basic | Document OCR + basic authenticity |
standard | + Face match + active liveness |
enhanced | + Anti-spoof + risk signals + compliance screening (sanctions / PEP / adverse media) |
Camera permissions
The SDK calls navigator.mediaDevices.getUserMedia({ video: true }) when capture starts. Browsers will show a permission prompt the first time. If the user denies, the SDK fires onError with KoraErrorCode.CAMERA_ACCESS_DENIED; you can prompt the user to update their browser settings.
QR handoff (desktop → mobile)
If your verification is initiated on desktop but you'd rather the user finish on their phone, use the QR handoff component:
import { QrHandoffScreen } from '@koraidv/react';
<QrHandoffScreen
verificationId={existingVerificationId}
onScanned={() => {/* user scanned, started on mobile */}}
/>
The mobile device can resume by opening the URL encoded in the QR code, which carries the verification ID and a short-lived token.
Security
- The SDK only accepts API keys with the
kora_sandbox_…orkora_live_…prefix. The key is sent on every request asAuthorization: Bearer <key>. - Treat your API key as a secret. Don't ship it in client-side code for production verifications. Instead, mint a short-lived session token from your backend and configure the SDK with that:
// Backend (Go SDK)
token, err := client.CreateSessionToken(ctx, externalId, "standard")
// Returns kora_session_… valid for 30 minutes, scoped to one verification// Frontend
new KoraIDV({ apiKey: token, tenantId, environment: 'production' }) - All communication is HTTPS. The SDK rejects requests over plain HTTP.
Bundle size
| Package | Min + gzip |
|---|---|
@koraidv/core | ~38 kB |
@koraidv/react | ~39 kB on top of core |
Heavy dependencies (TensorFlow, OpenCV WASM) are loaded lazily on first capture. Initial JS bundle stays small enough for most performance budgets.
Common issues
| Issue | Fix |
|---|---|
getUserMedia is not a function | Site must be served over HTTPS (or localhost). |
| Camera prompt not appearing | Ensure no other tab/page is holding the camera; clear the permission and reload. |
Module not found: @koraidv/core from React | Install both packages — @koraidv/core is a peer dep of @koraidv/react. |
| TypeScript can't find types | Both packages ship .d.ts files in dist/. Confirm your tsconfig.json moduleResolution is bundler or node16+. |
Next steps
- Sandbox testing — deterministic test fixtures
- Webhooks — server-side delivery of verification events
- Server integration — minting session tokens
- Image retrieval — accessing persisted images for compliance