JavaScript SDK

v1.0.0 ~4KB minified Zero dependencies TypeScript included

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

  1. HTTPS required — Callback URLs must use HTTPS in production (http://localhost is allowed for development).
  2. Server-side verification — Never trust client-side URL parameters. Always verify the session_id on your backend.
  3. Secret key protection — Keep sk_live_xxx on your server. Only use pk_live_xxx in client-side code.
  4. Domain validation — Xident validates that callback URLs match your API key's registered domain.

Browser Support

Browser Minimum Version
Chrome60+
Firefox55+
Safari12+
Edge79+

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