Web SDK
Drop-in verification flow for web apps. Modal or new-tab presentation, full TypeScript types, no peer dependencies. Requires a one-time session token from your backend.
About this SDK
The Width Web SDK is a drop-in verification experience for KYC, KYB, and one-off liveness in your web app. ESM build, full TypeScript types, no peer dependencies.
| Feature | Supported |
| KYC | ✓ |
| KYB | ✓ |
| One-off liveness | ✓ |
| TypeScript types | ✓ |
1. Generate an access token (backend)
Call sdk.credentials from your server using the OpenAPI signing scheme:
POST {WIDTH_GATEWAY_URL}/gateway?api=sdk.credentials&apiVersion=1.0.0&apiKey={your_apiKey}&sign=...
Content-Type: application/json
x-domain: {domainId}
{}
{
"apiCode": 200,
"apiMsg": "OK",
"data": {
"data": {
"sdkToken": "eyJhbGciOi...",
"domains": [
{ "code": "DEFAULT", "name": "Default", "id": "dom_xxxxxxxx" }
]
}
}
}
Each sdkToken is single-use. Reissue a new token whenever a user starts a new verification.
2. Install the SDK
npm install @width/web-sdk
# or
pnpm add @width/web-sdk
yarn add @width/web-sdk
3. Initialize
import { WidthWebSDK } from '@width/web-sdk';
const { sdkToken, domainId } =
await fetch('/your-backend/sdk-token').then(r => r.json());
WidthWebSDK.open({
apiKey: 'ak_your_public_api_key',
appToken: sdkToken,
email: 'enduser@example.com',
domainId,
mode: 'modal',
onResult: handleVerificationResult,
});
4. Handle the result
import type { VerificationResult } from '@width/web-sdk';
function handleVerificationResult(result: VerificationResult) {
switch (result.type) {
case 'COMPLETED':
if (result.decision === 'APPROVED') grantAccess(result.executionId);
else showDeclined(result.executionId);
break;
case 'WAITING':
persistExecutionId(result.executionId);
showPendingReview();
break;
case 'FAILED':
handleFailure(result.code, result.message);
break;
case 'CANCELLED':
break;
}
}
Launch options
Required
| Parameter | Type | Description |
apiKey | string | Your Width public API Key (ak_...) |
appToken | string | Session-level token from your backend |
email or customerId | string | At least one required |
Optional
| Parameter | Type | Default | Description |
customerId | string | — | Internal user ID |
email | string | — | Validated format |
domainId | string | Tenant default | Business unit ID |
mode | 'modal' \| 'newtab' | 'modal' | UI presentation |
zIndex | number | 9999 | Modal stacking order |
onResult | function | — | Result callback |
Methods
| Method | Purpose |
WidthWebSDK.open(options) | Launch the KYC / KYB flow |
WidthWebSDK.openLiveness(options) | Launch one-off liveness |
WidthWebSDK.close() | Programmatically close the modal |
openLiveness
const livenessToken = new URLSearchParams(window.location.search).get('token');
const { sdkToken, domainId } =
await fetch('/your-backend/sdk-token').then(r => r.json());
WidthWebSDK.openLiveness({
apiKey: 'ak_your_public_api_key',
appToken: sdkToken,
livenessToken,
domainId,
mode: 'modal',
onResult: handleVerificationResult,
});
Verification result
type VerificationResult =
| { type: 'COMPLETED'; executionId: string; decision: 'APPROVED' | 'REJECTED' }
| { type: 'WAITING'; executionId: string }
| { type: 'FAILED'; code: VerificationErrorCode; message: string; executionId?: string }
| { type: 'CANCELLED' };
type VerificationErrorCode = 'INVALID_INPUT' | 'SDK_ERROR' | 'WORKFLOW_FAILED';
| Code | Meaning | Retryable |
INVALID_INPUT | Launch parameter issue | No — fix code |
SDK_ERROR | Network / runtime error | Yes — retry |
WORKFLOW_FAILED | Non-success workflow state | No — contact support |
Browser support
| Platform | Minimum |
| Chrome / Edge | 92+ |
| Firefox | 95+ |
| Safari (desktop & iOS) | 15.4+ |
| Chrome (Android) | 92+ |
The SDK relies on crypto.randomUUID, ES2020 syntax, and modern fetch + postMessage. Older browsers are not supported.
CSP & Permissions Policy
Content-Security-Policy: frame-src https://<width-verify-origin>;
Permissions-Policy: camera=(self "https://<width-verify-origin>"),
microphone=(self "https://<width-verify-origin>"),
accelerometer=(self "https://<width-verify-origin>"),
gyroscope=(self "https://<width-verify-origin>")
In the Width Admin Console, add your domain under SSO & Integration → API Keys & Webhooks → Web SDK Settings → Allowed Origins.
Troubleshooting
| Symptom | Cause | Resolution |
apiKey is required. | Missing apiKey | Pass the public API Key |
appToken is required. | Backend issue | Check sdk.credentials integration |
email format is invalid. | Invalid format | Validate in your form first |
| apiKey / appToken mismatch | Different apps / tenants | Reissue token with the same key |
SDK_ERROR | Network issue | Retry open() |
| Modal won't appear | Domain not allowlisted / bad CSP | Add domain, update CSP |
| Camera denied | Strict Permissions-Policy | Update policy headers |
onResult never fires | Session ongoing or callback missing | Wait or confirm the callback |