JavaScript SDK
The JavaScript SDK (@xident/loader) is a tiny script that lets you add age verification to any website. It redirects users to verify.xident.io, where the full verification flow runs, and returns them to your site with a session ID for server-side verification.
Installation
Script Tag (Recommended)
The simplest way to get started. Add the script tag and a button — the SDK auto-configures from data-* attributes:
<script
src="https://sdk.xident.io/xident.min.js"
data-api-key="pk_live_your_api_key"
data-success-url="https://yoursite.com/verified"
data-failed-url="https://yoursite.com/denied"
></script>
<button onclick="Xident.verify()">Verify Your Age</button> That's it. When the user clicks the button, they're redirected to complete verification and returned to your success or failed URL.
CDN URLs
<!-- unpkg -->
<script src="https://unpkg.com/@xident/loader/dist/xident.min.js"></script>
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@xident/loader/dist/xident.min.js"></script>
<!-- Xident CDN (recommended) -->
<script src="https://sdk.xident.io/xident.min.js"></script> npm
npm install @xident/loader import { Xident } from '@xident/loader';
Xident.configure({
apiKey: 'pk_live_your_api_key',
successUrl: 'https://yoursite.com/verified',
failedUrl: 'https://yoursite.com/denied',
});
// Trigger verification
Xident.verify(); Configuration
Script Tag Attributes
When using the script tag, configuration is read automatically from data-* attributes:
<script
src="https://sdk.xident.io/xident.min.js"
data-api-key="pk_live_your_api_key"
data-success-url="https://yoursite.com/verified"
data-failed-url="https://yoursite.com/denied"
data-min-age="21"
data-theme="dark"
data-locale="de"
data-user-id="user-123"
></script> | Attribute | Required | Description |
|---|---|---|
data-api-key | Yes | Your public API key (pk_live_xxx). Get it from the dashboard. |
data-success-url | Yes* | URL to redirect on successful verification. |
data-failed-url | Yes* | URL to redirect on failed verification. |
data-callback-url | No | Legacy single callback URL. Use data-success-url + data-failed-url instead. |
data-min-age | No | Age threshold. Default: 18. |
data-theme | No | light, dark, or auto (matches user's system preference). |
data-locale | No | Language code. Supported: en, es, de, fr, it, pt, nl, pl, tr, ar, ja, ko. |
data-user-id | No | Your internal user identifier. |
data-verify-url | No | Custom verification URL. Default: https://verify.xident.io. |
*Either data-success-url + data-failed-url, or data-callback-url is required.
Programmatic Configuration
For SPAs or when you need dynamic configuration:
Xident.configure({
apiKey: 'pk_live_your_api_key',
successUrl: 'https://yoursite.com/verified',
failedUrl: 'https://yoursite.com/denied',
minAge: 21, // Age threshold (default: 18)
theme: 'dark', // 'light' | 'dark' | 'auto'
locale: 'de', // Language code
userId: 'user-123', // Your internal user ID
}); Legacy: Single Callback URL
// Legacy: single callback URL for all outcomes
Xident.configure({
apiKey: 'pk_live_your_api_key',
callbackUrl: 'https://yoursite.com/done',
}); Starting Verification
// Basic — just redirect
Xident.verify();
// With user tracking
Xident.verify({
userId: 'user-123'
});
// With metadata (available in token response)
Xident.verify({
userId: 'user-123',
metadata: {
orderId: 'order-456',
plan: 'premium'
}
});
// Open in new tab instead of redirect
Xident.verify({
userId: 'user-123',
newTab: true
});
// Backward compatible alias
Xident.start(); // Same as verify() | Option | Type | Description |
|---|---|---|
userId | string | Your internal user ID. Included in the session for matching. Auto-generated UUIDv7 if omitted. |
metadata | Record<string, string> | Custom key-value pairs. Base64-encoded and available in the session response. |
newTab | boolean | Open verification in a new tab instead of navigating away. Default: false. |
Server-to-Server Verification
CRITICAL: Never trust client-side URL parameters for authorization decisions. Always verify the session_id on your backend using your secret API key (sk_live_xxx).
After verification, the user is redirected back to your site with a session_id in the URL. Use getSessionFromUrl() to extract it, then send it to your backend:
// On your callback page — extract session info from URL
const session = Xident.getSessionFromUrl();
// Returns: { sessionId: 'sess_xxx', status: 'completed', result: 'confirmed' }
// Returns null if no session_id in URL
if (session) {
// Send to YOUR backend for server-side verification
const res = await fetch('/api/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sessionId: session.sessionId })
});
const result = await res.json();
// result.verified, result.age_bracket, etc.
} Backend Verification
Your backend calls GET /v1/status/{session_id} with your secret API key:
Node.js
// Node.js — verify session on your backend
const response = await fetch(
`https://api.xident.io/v1/status/${sessionId}`,
{ headers: { 'X-API-Key': process.env.XIDENT_SECRET_KEY } }
);
const result = await response.json();
if (result.data.status === 'completed' && result.data.result === 'confirmed') {
// User passed verification — grant access
} Go
// Go
req, _ := http.NewRequest("GET",
fmt.Sprintf("https://api.xident.io/v1/status/%s", sessionID), nil)
req.Header.Set("X-API-Key", os.Getenv("XIDENT_SECRET_KEY"))
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var result struct {
Data struct {
Status string `json:"status"`
Result string `json:"result"`
} `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&result)
if result.Data.Status == "completed" && result.Data.Result == "confirmed" {
// User passed verification
} PHP
// PHP
$response = file_get_contents(
"https://api.xident.io/v1/status/{$sessionId}",
false,
stream_context_create([
'http' => [
'header' => "X-API-Key: " . getenv('XIDENT_SECRET_KEY'),
],
])
);
$result = json_decode($response, true);
if ($result['data']['status'] === 'completed' && $result['data']['result'] === 'confirmed') {
// User passed verification
} Python
# Python
import os, requests
response = requests.get(
f"https://api.xident.io/v1/status/{session_id}",
headers={"X-API-Key": os.environ["XIDENT_SECRET_KEY"]},
)
result = response.json()
if result["data"]["status"] == "completed" and result["data"]["result"] == "confirmed":
# User passed verification Optional JavaScript Callbacks
For analytics tracking before redirect (these fire client-side, not a substitute for server verification):
// Optional: fire analytics before redirect
Xident.configure({
apiKey: 'pk_live_xxx',
successUrl: 'https://yoursite.com/success',
failedUrl: 'https://yoursite.com/failed',
onVerified: (result) => {
analytics.track('verification_passed', { sessionId: result.sessionId });
},
onFailed: (result) => {
analytics.track('verification_failed', { sessionId: result.sessionId });
},
}); Framework Examples
React
// components/VerifyButton.tsx
import { useEffect, useState } from 'react';
declare global {
interface Window { Xident: any; }
}
export function VerifyButton({ userId }: { userId?: string }) {
const [ready, setReady] = useState(false);
useEffect(() => {
const check = () => {
if (window.Xident?.isConfigured()) {
setReady(true);
} else if (window.Xident) {
window.Xident.configure({
apiKey: import.meta.env.VITE_XIDENT_API_KEY,
successUrl: `${window.location.origin}/verified`,
failedUrl: `${window.location.origin}/denied`,
});
setReady(true);
}
};
if (document.readyState === 'complete') check();
else window.addEventListener('load', check);
return () => window.removeEventListener('load', check);
}, []);
return (
<button onClick={() => window.Xident.verify({ userId })} disabled={!ready}>
{ready ? 'Verify Age' : 'Loading…'}
</button>
);
} Next.js
// components/VerifyButton.tsx
'use client';
import { useState } from 'react';
import Script from 'next/script';
export function VerifyButton({ userId }: { userId: string }) {
const [ready, setReady] = useState(false);
return (
<>
<Script
src="https://sdk.xident.io/xident.min.js"
onLoad={() => {
window.Xident.configure({
apiKey: process.env.NEXT_PUBLIC_XIDENT_API_KEY!,
successUrl: `${window.location.origin}/verified`,
failedUrl: `${window.location.origin}/denied`,
});
setReady(true);
}}
/>
<button onClick={() => window.Xident.verify({ userId })} disabled={!ready}>
Verify Age
</button>
</>
);
} Vue 3
<!-- components/VerifyButton.vue -->
<template>
<button @click="verify" :disabled="!ready">
{{ ready ? 'Verify Age' : 'Loading…' }}
</button>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
const props = defineProps<{ userId?: string }>();
const ready = ref(false);
onMounted(() => {
const init = () => {
if (window.Xident) {
if (!window.Xident.isConfigured()) {
window.Xident.configure({
apiKey: import.meta.env.VITE_XIDENT_API_KEY,
successUrl: `${window.location.origin}/verified`,
failedUrl: `${window.location.origin}/denied`,
});
}
ready.value = true;
}
};
if (document.readyState === 'complete') init();
else window.addEventListener('load', init);
});
const verify = () => window.Xident.verify({ userId: props.userId });
</script> Svelte
<!-- VerifyButton.svelte -->
<script lang="ts">
import { onMount } from 'svelte';
export let userId: string | undefined = undefined;
let ready = false;
onMount(() => {
const init = () => {
if (window.Xident) {
if (!window.Xident.isConfigured()) {
window.Xident.configure({
apiKey: import.meta.env.VITE_XIDENT_API_KEY,
successUrl: `${window.location.origin}/verified`,
failedUrl: `${window.location.origin}/denied`,
});
}
ready = true;
}
};
if (document.readyState === 'complete') init();
else window.addEventListener('load', init);
});
const verify = () => window.Xident.verify({ userId });
</script>
<button on:click={verify} disabled={!ready}>
{ready ? 'Verify Age' : 'Loading…'}
</button> Helper Methods
// Check if SDK is configured and ready
if (Xident.isConfigured()) {
Xident.verify();
} else {
console.log('SDK not ready yet');
}
// Get current configuration (read-only copy)
const config = Xident.getConfig();
console.log(config?.apiKey); // 'pk_live_...'
console.log(config?.minAge); // 21
console.log(config?.theme); // 'dark'
// SDK version
console.log(Xident.version); // '1.0.0' API Reference
| Method / Property | Returns | Description |
|---|---|---|
Xident.verify(options?) | void | Redirect user to the verification flow. Throws if not configured. |
Xident.start(options?) | void | Alias for verify() (backward compatible). |
Xident.configure(config) | void | Manually set configuration. Validates apiKey and URLs. |
Xident.isConfigured() | boolean | Returns true if apiKey and callback mechanism are set. |
Xident.getConfig() | XidentConfig | null | Returns a read-only copy of the current configuration. |
Xident.getSessionFromUrl() | SessionResult | null | Extract session ID and result from current URL parameters. |
Xident.version | string | SDK version (currently "1.0.0"). |
TypeScript
Full type definitions are included. If you're using the script tag (not npm), declare the global:
interface XidentConfig {
apiKey: string; // Required
successUrl?: string; // Redirect on pass
failedUrl?: string; // Redirect on fail
callbackUrl?: string; // Legacy: single callback
verifyUrl?: string; // Default: verify.xident.io
theme?: 'light' | 'dark' | 'auto';
locale?: string; // e.g. 'en', 'es', 'de'
minAge?: number; // Default: 18
userId?: string; // Your internal user ID
onVerified?: (result: SessionResult) => void;
onFailed?: (result: SessionResult) => void;
}
interface VerifyOptions {
userId?: string;
metadata?: Record<string, string>;
newTab?: boolean;
}
interface SessionResult {
sessionId: string;
status: string;
result: string;
}
interface XidentInstance {
verify(options?: VerifyOptions): void;
start(options?: VerifyOptions): void; // Alias for verify
configure(config: XidentConfig): void;
isConfigured(): boolean;
getConfig(): Readonly<XidentConfig> | null;
getSessionFromUrl(): SessionResult | null;
version: string;
}
// Available globally as window.Xident
declare global {
interface Window { Xident: XidentInstance; }
} Error Handling
try {
Xident.verify();
} catch (error) {
if (error.message.includes('not configured')) {
// SDK hasn't been initialized — call configure() first
} else if (error.message.includes('successUrl') || error.message.includes('callbackUrl')) {
// Missing callback URLs
} else if (error.message.includes('Invalid')) {
// URL must be https:// (or http://localhost for dev)
}
} Testing & Development
<!-- Test mode: http://localhost is allowed for callback URLs -->
<script
src="https://sdk.xident.io/xident.min.js"
data-api-key="pk_test_xxx"
data-success-url="http://localhost:3000/verified"
data-failed-url="http://localhost:3000/denied"
></script>
<script>
// Debug helpers
console.log('Version:', Xident.version); // '1.0.0'
console.log('Ready:', Xident.isConfigured()); // true/false
console.log('Config:', Xident.getConfig()); // { apiKey, ... }
</script> Test API keys (pk_test_xxx) skip real verification and always return success. Use them for development and CI.
Security
- HTTPS required — Callback URLs must use HTTPS in production (
http://localhostis allowed for development). - Server-side verification — Never trust client-side URL parameters. Always verify the
session_idon your backend. - Secret key protection — Keep
sk_live_xxxon your server. Only usepk_live_xxxin client-side code. - Domain validation — Xident validates that callback URLs match your API key's registered domain.
Browser Support
| Browser | Minimum Version |
|---|---|
| Chrome | 60+ |
| Firefox | 55+ |
| Safari | 12+ |
| Edge | 79+ |
The SDK targets ES2015 and uses only standard browser APIs (URL, URLSearchParams, btoa, crypto.getRandomValues).
Architecture
Developer's Website Xident Platform
┌────────────────────────────┐ ┌───────────────────────┐
│ xident.min.js (~4KB) │ ──redirect──▶ │ verify.xident.io │
│ │ │ │
│ 1. User clicks "Verify" │ │ 2. Liveness detection │
│ 2. SDK builds verification URL│ │ 3. Age bracket check │
│ 3. Redirects to verify.xident│ │ 4. Document OCR (if needed)│
└────────────────────────────┘ └───────────────────────┘
┌────────────────────────────┐ │
│ Your callback URL │ ◀──redirect + session_id──┘
│ │
│ 4. Extract session_id │
│ 5. Verify server-to-server │
│ 6. Grant or deny access │
└────────────────────────────┘ The SDK itself performs no verification logic. It's purely a redirect orchestrator (~4KB). All ML models, liveness detection, and document processing run on the Xident platform.
Related
- All SDKs — Overview of all Xident SDKs
- Node.js SDK — Server-side token verification for Node.js
- Python SDK — Server-side token verification for Python
- Quick Start — End-to-end 5-minute setup
- API Reference — Full REST API documentation