Prerequisites
Call the Create Journey API from your backend to obtain a journeyToken.
Installation
Load the SDK via script tag:
<script src="https://your-cdn-url.com/path/to/index.js" type="module"></script>
Add the SDK container to your page:
<div id="uae-kyc-container"></div>
The SDK must be loaded with type="module". When self-hosting, mirror the entire SDK version directory — not just the main JavaScript file — and serve all files with correct MIME types and CORS headers.
Complete Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>UAE KYC Integration</title>
<script src="https://your-cdn-url.com/path/to/index.js" type="module"></script>
<style>
#uae-kyc-container { width: 100%; height: 100vh; display: none; }
body, html { margin: 0; padding: 0; height: 100%; width: 100%; }
#controls-container { padding: 20px; text-align: center; }
#start-kyc { padding: 10px 20px; font-size: 16px; cursor: pointer; }
</style>
</head>
<body>
<div id="controls-container">
<h1>UAE KYC Integration</h1>
<button id="start-kyc">Start KYC Process</button>
</div>
<div id="uae-kyc-container"></div>
<script>
document.getElementById('start-kyc').addEventListener('click', function () {
const { startJourney, Status } = window.UAEKYC;
const config = {
journeyToken: "<JOURNEY_TOKEN>",
language: "en",
theme: "system",
apiDomain: "your-api-domain.ae",
privacyPolicyUrl: "https://example.ae/privacy",
logoUrl: "https://example.ae/logo",
accentColor: "#CFB16C"
};
document.getElementById('controls-container').style.display = 'none';
document.getElementById('uae-kyc-container').style.display = 'block';
startJourney(config, function (result) {
document.getElementById('controls-container').style.display = 'block';
document.getElementById('uae-kyc-container').style.display = 'none';
if (result.status === Status.Success) {
alert("KYC completed successfully.");
} else {
alert("KYC ended with status: " + Status[result.status]);
}
});
});
</script>
</body>
</html>
Server & CDN Configuration
When self-hosting the SDK, configure your server with the correct MIME types, compression, cache headers, and permission policies.
MIME Types
| Extension | MIME Type | Description |
|---|
.js, .mjs | application/javascript | JavaScript modules |
.wasm | application/wasm | WebAssembly binaries |
.html | text/html | HTML documents |
.css | text/css | Stylesheets |
.json | application/json | Configuration files |
.png | image/png | PNG images |
.jpg, .jpeg | image/jpeg | JPEG images |
.svg | image/svg+xml | SVG graphics |
.woff2 | font/woff2 | Web fonts |
.bin, .task | application/octet-stream | ML models and binary assets |
Compression
| Format | Recommended | Notes |
|---|
| Brotli | Yes (preferred) | 15-25% better compression than Gzip |
| Gzip | Yes (fallback) | Universal browser support |
Compress these types: text/plain, text/css, application/javascript, application/json, application/wasm, image/svg+xml
Do not compress already-compressed formats (PNG, JPEG, WOFF2). Binary files (.bin, .task) may be pre-compressed or encrypted — test whether compression provides benefit.
Cache-Control
| Asset Type | Max-Age | Cache-Control Header |
|---|
| JS, CSS, WASM, Fonts | 1 year | public, max-age=31536000, immutable |
| Images (PNG, JPEG, SVG) | 30 days | public, max-age=2592000 |
| HTML | 1 hour | public, max-age=3600, must-revalidate |
| JSON | 1 day | public, max-age=86400, must-revalidate |
Permission Policy
The Web SDK requires camera access for identity verification. You must configure the Permission-Policy header on your server or CDN to allow camera access.Without this header, the browser will block camera access and the SDK will fail during document capture and face verification steps.
Add the following header to your server configuration:
Permission-Policy: camera=(self)
For more details, see MDN: Permissions-Policy camera.
Sample NGINX Configuration
# MIME types
types {
application/javascript js mjs;
application/wasm wasm;
text/html html;
text/css css;
application/json json;
image/png png;
image/jpeg jpeg jpg;
image/svg+xml svg;
font/woff2 woff2;
application/octet-stream bin task;
}
# Gzip compression
gzip on;
gzip_vary on;
gzip_comp_level 6;
gzip_min_length 256;
gzip_types text/plain text/css application/javascript application/json application/wasm image/svg+xml;
# Brotli compression (requires ngx_brotli module)
brotli on;
brotli_comp_level 6;
brotli_min_length 256;
brotli_types text/plain text/css application/javascript application/json application/wasm image/svg+xml;
# Cache-Control headers
location ~* \.(js|mjs|css|wasm|woff2|bin|task)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~* \.(png|jpg|jpeg|svg)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public, max-age=3600, must-revalidate";
}
location ~* \.json$ {
expires 1d;
add_header Cache-Control "public, max-age=86400, must-revalidate";
}
# Permission Policy
add_header 'Permission-Policy' 'camera=(self)' always;