Skip to main content

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

ExtensionMIME TypeDescription
.js, .mjsapplication/javascriptJavaScript modules
.wasmapplication/wasmWebAssembly binaries
.htmltext/htmlHTML documents
.csstext/cssStylesheets
.jsonapplication/jsonConfiguration files
.pngimage/pngPNG images
.jpg, .jpegimage/jpegJPEG images
.svgimage/svg+xmlSVG graphics
.woff2font/woff2Web fonts
.bin, .taskapplication/octet-streamML models and binary assets

Compression

FormatRecommendedNotes
BrotliYes (preferred)15-25% better compression than Gzip
GzipYes (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 TypeMax-AgeCache-Control Header
JS, CSS, WASM, Fonts1 yearpublic, max-age=31536000, immutable
Images (PNG, JPEG, SVG)30 dayspublic, max-age=2592000
HTML1 hourpublic, max-age=3600, must-revalidate
JSON1 daypublic, 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;