> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript SDK

> Integrate Didit identity verification into any web app with our JavaScript SDK. Embedded, redirect, or iframe modes. Pay-per-call from $0.30, 500 free/month.

export const AgentPromptAccordion = ({prompt, title = "AI Agent Integration Prompt"}) => {
  const [copied, setCopied] = React.useState(false);
  const handleCopy = e => {
    e.stopPropagation();
    if (!prompt) return;
    navigator.clipboard.writeText(prompt.trim()).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };
  const agents = ["Claude Code", "Codex", "Cursor", "Devin", "Windsurf", "GitHub Copilot"];
  return <div className="didit-agent-card">
      {}
      <div className="didit-agent-titlebar">
        <div className="didit-agent-dots" aria-hidden="true">
          <span className="didit-agent-dot didit-agent-dot-red"></span>
          <span className="didit-agent-dot didit-agent-dot-yellow"></span>
          <span className="didit-agent-dot didit-agent-dot-green"></span>
        </div>
        <span className="didit-agent-filename">{title}</span>
        <button type="button" className={`didit-agent-copy ${copied ? "didit-agent-copy-copied" : ""}`} onClick={handleCopy} title="Copy prompt to clipboard" aria-label={copied ? "Copied!" : "Copy prompt to clipboard"}>
          {copied ? <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <path d="M3 8.5l3.5 3.5L13 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
              <span>Copied</span>
            </> : <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <rect x="5" y="5" width="9" height="9" rx="1.5" stroke="currentColor" strokeWidth="1.5" />
                <path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5" stroke="currentColor" strokeWidth="1.5" />
              </svg>
              <span>Copy</span>
            </>}
        </button>
      </div>

      {}
      <pre className="didit-agent-body"><code>{prompt.trim()}</code></pre>

      {}
      <div className="didit-agent-footer">
        <span className="didit-agent-footer-label">Paste into</span>
        <div className="didit-agent-chips">
          {agents.map(name => <span key={name} className="didit-agent-chip">{name}</span>)}
        </div>
      </div>
    </div>;
};

<AgentPromptAccordion
  title="JavaScript SDK Integration Prompt"
  prompt={`Integrate Didit identity verification into my web application using the JavaScript SDK.

## Setup
Install: npm install @didit-protocol/sdk-web
Or CDN: <script src="https://unpkg.com/@didit-protocol/sdk-web/dist/didit-sdk.umd.min.js"></script>

## Integration Flow
1. (Option A — UniLink) Use a fixed UniLink URL from the Didit Console (no backend needed)
2. (Option B — API session) Backend creates a session via POST /v3/session/ → 201 response includes "url"
3. Frontend calls DiditSdk.shared.startVerification({ url }) — the SDK opens a modal (or inline if embedded mode)
4. On completion, the SDK invokes DiditSdk.shared.onComplete(result) with type "completed" | "cancelled" | "failed"
5. Backend receives full signed decision via webhook (status.updated)

## Frontend Code — Vanilla JS / TypeScript
import { DiditSdk } from '@didit-protocol/sdk-web';

DiditSdk.shared.onComplete = (result) => {
switch (result.type) {
case 'completed':
  // result.session.status: "Approved" | "Pending" | "Declined"
  console.log('Done:', result.session.sessionId, result.session.status);
  break;
case 'cancelled':
  console.log('User cancelled');
  break;
case 'failed':
  console.error('Failed:', result.error.type, result.error.message);
  break;
}
};

// Modal mode (default)
DiditSdk.shared.startVerification({
url: 'https://verify.didit.me/session/SESSION_TOKEN' // from backend OR UniLink
});

// Inline / embedded mode
DiditSdk.shared.startVerification({
url: 'https://verify.didit.me/session/SESSION_TOKEN',
configuration: { embedded: true, embeddedContainerId: 'didit-container' }
});

## Frontend Code — React
function VerifyButton({ userId }) {
const [loading, setLoading] = useState(false);

async function handleVerify() {
setLoading(true);
const res = await fetch('/api/create-session', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ user_id: userId })
});
const { url } = await res.json(); // OpenAPI field is named "url"

const { DiditSdk } = await import('@didit-protocol/sdk-web');
DiditSdk.shared.onComplete = (result) => {
  if (result.type === 'completed') {
    // update UI; final, signed result arrives on backend via webhook
  }
  setLoading(false);
};
DiditSdk.shared.startVerification({ url });
}

return <button onClick={handleVerify} disabled={loading}>Verify Identity</button>;
}

## Backend: Create Session
POST https://verification.didit.me/v3/session/
Headers: { "x-api-key": DIDIT_API_KEY, "Content-Type": "application/json" }
Body: { "workflow_id": DIDIT_WORKFLOW_ID, "callback": "https://myapp.com/done", "vendor_data": userId }
Returns 201 (all 10 fields, per OpenAPI):
{
"session_id": "uuid",
"session_number": 43762,
"session_token": "3FaJ9wLqX2Mz",   // 12-char URL-safe token; valid until the session expires
"url": "https://verify.didit.me/session/3FaJ9wLqX2Mz",
"vendor_data": "userId",
"metadata": null,
"status": "Not Started",
"workflow_id": "uuid",
"workflow_version": 3,
"callback": "https://myapp.com/done"
}
Pass the "url" field to DiditSdk.shared.startVerification({ url }) — never build the URL by hand.
(If the request sets "language", the URL gains a language segment: https://verify.didit.me/es/session/...)

## SDK Surface (singleton — DiditSdk.shared)
Methods:  startVerification(options), close(), destroy()
Callbacks: onComplete(result), onStateChange(state, error?), onEvent(event)
State enum: 'idle' | 'loading' | 'ready' | 'error'

## Rate Limits
600 session-create requests per minute per API key (falls back to bearer token, then client IP), plus a shared budget of 300 write (POST/PATCH/DELETE) requests per minute across all endpoints — the 300/min budget is exhausted first. Exceeding either returns HTTP 429 with Retry-After.

## Environment Variables
- DIDIT_API_KEY — backend only (Didit Console > API & Webhooks)
- DIDIT_WORKFLOW_ID — backend only (Didit Console > Workflows)
- Never expose the API key on the frontend — the session_token embedded in "url" is the only credential the SDK needs. It grants access to that one session's flow (valid until the session expires), so don't log or share it either.

## Docs
- JavaScript SDK: https://docs.didit.me/integration/web-sdks/javascript-sdk
- Create Session API: https://docs.didit.me/sessions-api/create-session
- Webhooks: https://docs.didit.me/integration/webhooks
`}
/>

The Didit JavaScript SDK provides a programmatic way to integrate verification into your web application with full control over the user experience.

***

## Features

* **Simple API** - Singleton pattern with easy-to-use methods
* **Flexible Integration** - Use UniLink URL directly or create sessions via backend
* **Responsive** - Works on desktop and mobile browsers
* **Customizable** - Configuration options for styling and behavior
* **Multiple Formats** - ESM, CommonJS, and UMD builds
* **TypeScript Support** - Full type definitions included
* **Modal & Inline Modes** - Choose between modal overlay or inline embedding
* **Event Callbacks** - Real-time events for verification progress (started, step completed, finished)
* **State Management** - Observe SDK state for custom UI

***

## Installation

### NPM/Yarn

```bash theme={null}
npm install @didit-protocol/sdk-web
# or
yarn add @didit-protocol/sdk-web
```

### CDN (UMD)

```html theme={null}
<script src="https://unpkg.com/@didit-protocol/sdk-web/dist/didit-sdk.umd.min.js"></script>
```

***

## Vanilla JavaScript

### ES Modules / TypeScript

```typescript theme={null}
import { DiditSdk } from '@didit-protocol/sdk-web';

// Set up completion callback
DiditSdk.shared.onComplete = (result) => {
  switch (result.type) {
    case 'completed':
      console.log('Verification completed!');
      console.log('Session ID:', result.session?.sessionId);
      console.log('Status:', result.session?.status);
      break;
    case 'cancelled':
      console.log('User cancelled verification');
      break;
    case 'failed':
      console.error('Verification failed:', result.error?.message);
      break;
  }
};

// Start verification with the `url` returned by POST /v3/session/ (or your UniLink)
DiditSdk.shared.startVerification({
  url: 'https://verify.didit.me/session/3FaJ9wLqX2Mz'
});
```

### Script Tag (UMD)

```html theme={null}
<script src="https://unpkg.com/@didit-protocol/sdk-web/dist/didit-sdk.umd.min.js"></script>
<script>
  const { DiditSdk } = DiditSDK;

  DiditSdk.shared.onComplete = (result) => {
    if (result.type === 'completed') {
      alert('Verification completed: ' + result.session.status);
    }
  };

  function startVerification() {
    DiditSdk.shared.startVerification({
      // You can get this link by clicking on "copy link" in the workflows view
      url: 'https://verify.didit.me/u/WORKFLOW_ID_IN_BASE_64'
    });
  }
</script>

<button onclick="startVerification()">Verify Identity</button>
```

***

## Framework Examples

<Card title="SDK Web Examples" icon="github" href="https://github.com/didit-protocol/sdk-web-examples">
  React, Angular, Svelte, Next.js, Nuxt, Vue, and more — with examples and documentation for each framework.
</Card>

***

## Integration Methods

There are two ways to integrate the SDK:

### Method 1: UniLink URL (Simplest)

Use your workflow's UniLink URL directly from the Didit Console. No backend required.

```typescript theme={null}
DiditSdk.shared.startVerification({
  // You can get this link by clicking on "copy link" in the workflows view
  url: 'https://verify.didit.me/u/WORKFLOW_ID_IN_BASE_64'
});
```

Get your UniLink URL from the [Didit Console](https://business.didit.me) → Your Workflow → Copy Link.

**The session\_id generated will be sent to you by an event.**
**[Check event reference here](#event-reference)**

### Method 2: Backend session (recommended for production)

Your backend creates a session via the Didit API and returns the hosted verification URL. This gives you control over `vendor_data`, per-session `callback`, `metadata`, and lets you create sessions before the user reaches the frontend.

Read more in the [Create Session API reference](/sessions-api/create-session).

**Backend (Node.js / Express)**

```javascript theme={null}
import express from 'express';
const app = express();
app.use(express.json());

app.post('/api/create-session', async (req, res) => {
  const response = await fetch('https://verification.didit.me/v3/session/', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.DIDIT_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      workflow_id: process.env.DIDIT_WORKFLOW_ID,
      vendor_data: req.body.userId,             // links the session to your user
      callback: 'https://yourapp.com/verified', // optional — overrides workflow default
    }),
  });

  if (!response.ok) {
    return res.status(response.status).send(await response.text());
  }

  // 201 response fields (per OpenAPI): session_id, session_number, session_token, url,
  // vendor_data, metadata, status, workflow_id, workflow_version, callback
  const session = await response.json();
  res.json({ session_id: session.session_id, url: session.url });
});
```

**Frontend**

```typescript theme={null}
import { DiditSdk } from '@didit-protocol/sdk-web';

const res = await fetch('/api/create-session', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ userId: 'user-123' }),
});
const { url } = await res.json();

DiditSdk.shared.startVerification({
  url,
  configuration: {
    loggingEnabled: true,
  },
});
```

<Note>
  The session-create response field is named `url` (see the [Create Session OpenAPI schema](/sessions-api/create-session)). Pass it directly to `startVerification({ url })`.
</Note>

***

## Configuration

| Option                 | Type          | Default         | Description                                                                 |
| ---------------------- | ------------- | --------------- | --------------------------------------------------------------------------- |
| `loggingEnabled`       | `boolean`     | `false`         | Enable SDK logging for debugging                                            |
| `containerElement`     | `HTMLElement` | `document.body` | Custom container element to mount the modal                                 |
| `zIndex`               | `number`      | `9999`          | Z-index for the modal overlay                                               |
| `showCloseButton`      | `boolean`     | `true`          | Show close button on modal                                                  |
| `showExitConfirmation` | `boolean`     | `true`          | Show exit confirmation dialog when closing                                  |
| `closeModalOnComplete` | `boolean`     | `false`         | Automatically close modal when verification completes                       |
| `embedded`             | `boolean`     | `false`         | Render verification inline instead of modal overlay                         |
| `embeddedContainerId`  | `string`      | —               | Container element ID for embedded mode (required when `embedded` is `true`) |

<Tip>See the full [TypeScript type definitions](#typescript-types) below for the complete `DiditSdkConfiguration` interface.</Tip>

***

## Embedded Mode

Render verification inline instead of a modal overlay:

```html theme={null}
<div id="verification-container" style="width: 500px; height: 700px;"></div>
```

```typescript theme={null}
DiditSdk.shared.startVerification({
  url: 'https://verify.didit.me/u/...',
  configuration: {
    embedded: true,
    embeddedContainerId: 'verification-container'
  }
});
```

***

## Verification Results

The SDK returns three types of results:

### Completed

Verification flow finished (approved, pending, or declined).

```typescript theme={null}
{
  type: 'completed',
  session: {
    sessionId: 'session-uuid',
    status: 'Approved' | 'Pending' | 'Declined'
  }
}
```

<Note>
  The TypeScript type declares `'Approved' | 'Pending' | 'Declined'`, but the value is passed through from the hosted flow's session status — you can also receive other statuses such as `In Review`. `Pending` is the SDK's fallback when the flow reported no status. Treat `status` as a hint and rely on the [webhook](/integration/webhooks) for the canonical decision.
</Note>

### Cancelled

User closed the verification modal. `session` is included when a `sessionId` was already known (it can be `undefined` if the user cancelled before the iframe reported one).

```typescript theme={null}
{
  type: 'cancelled',
  session: {
    sessionId: 'session-uuid',
    status: 'Pending'
  }
}
```

### Failed

An error occurred during verification.

```typescript theme={null}
{
  type: 'failed',
  error: {
    type: 'sessionExpired' | 'networkError' | 'cameraAccessDenied' | 'unknown',
    message: 'Your verification session has expired.'
  }
}
```

***

## State Management

You can observe the SDK state for custom UI:

```typescript theme={null}
DiditSdk.shared.onStateChange = (state, error) => {
  switch (state) {
    case 'idle':
      // Ready to start
      break;
    case 'loading':
      // Loading verification iframe
      showLoadingSpinner();
      break;
    case 'ready':
      // Verification in progress
      hideLoadingSpinner();
      break;
    case 'error':
      // Error occurred
      showError(error);
      break;
  }
};

// Check current state
console.log(DiditSdk.shared.state);

// Check if verification is presented
console.log(DiditSdk.shared.isPresented);
```

***

## API Reference

### DiditSdk.shared

The singleton SDK instance.

### Methods

| Method                       | Description                                   |
| ---------------------------- | --------------------------------------------- |
| `startVerification(options)` | Start the verification flow                   |
| `close()`                    | Programmatically close the verification modal |
| `destroy()`                  | Destroy the SDK instance and clean up         |

### Properties

| Property        | Type                                 | Description                                                    |
| --------------- | ------------------------------------ | -------------------------------------------------------------- |
| `state`         | `DiditSdkState`                      | Current SDK state                                              |
| `configuration` | `DiditSdkConfiguration \| undefined` | Active configuration (set while a verification is in progress) |
| `isPresented`   | `boolean`                            | Whether verification modal is open                             |
| `errorMessage`  | `string \| undefined`                | Error message (set when state is `error`)                      |

### Callbacks

| Callback        | Parameters                               | Description                            |
| --------------- | ---------------------------------------- | -------------------------------------- |
| `onComplete`    | `(result: VerificationResult)`           | Called when verification finishes      |
| `onStateChange` | `(state: DiditSdkState, error?: string)` | Called on state changes                |
| `onEvent`       | `(event: VerificationEvent)`             | Called on granular verification events |

***

## Granular Events

Track verification progress with the `onEvent` callback:

```typescript theme={null}
DiditSdk.shared.onEvent = (event) => {
  console.log('Event:', event.type, event.data);
};
```

### Event Reference

| Event                          | Description                                      | Data Payload                                       |
| ------------------------------ | ------------------------------------------------ | -------------------------------------------------- |
| `didit:ready`                  | The page is ready and the sessionId is available | `{ sessionId }`                                    |
| `didit:started`                | User started verification                        | `{ sessionId? }`                                   |
| `didit:step_started`           | A verification step began                        | `{ step, sessionId? }`                             |
| `didit:step_completed`         | A step finished successfully                     | `{ step, nextStep?, sessionId? }`                  |
| `didit:step_changed`           | Current step changed                             | `{ step, previousStep?, sessionId? }`              |
| `didit:media_started`          | Camera/media capture started                     | `{ step, mediaType }`                              |
| `didit:media_captured`         | Photo/video captured                             | `{ step, isAuto }`                                 |
| `didit:document_selected`      | User selected document type and country          | `{ documentType, country }`                        |
| `didit:verification_submitted` | Data submitted for processing                    | `{ step, sessionId? }`                             |
| `didit:code_sent`              | OTP code sent                                    | `{ step, channel?, codeSize?, sessionId? }`        |
| `didit:code_verified`          | OTP code verified                                | `{ step, sessionId? }`                             |
| `didit:status_updated`         | Session status changed                           | `{ status, previousStep?, sessionId? }`            |
| `didit:completed`              | Verification flow completed                      | `{ sessionId?, status?, country?, documentType? }` |
| `didit:cancelled`              | User cancelled verification                      | `{ sessionId? }`                                   |
| `didit:error`                  | An error occurred                                | `{ error: string, step?, sessionId? }`             |
| `didit:close_request`          | User requested to close modal                    | (no data)                                          |

### Step Values

The `step` field can be one of:

* `document_selection` - Document type selection
* `document_front` - Front side of document
* `document_back` - Back side of document
* `face` - Face/liveness verification
* `email` - Email verification
* `phone` - Phone verification
* `poa` - Proof of address
* `questionnaire` - Questionnaire step

### Channel Values

The `channel` field in `code_sent` can be:

* `email` - Code sent via email
* `sms` - Code sent via SMS
* `whatsapp` - Code sent via WhatsApp

### Code Size

The `codeSize` field in `code_sent` indicates the OTP code length (e.g., 4 or 6 digits).

***

## TypeScript Types

```typescript theme={null}
// Configuration types
interface DiditSdkConfiguration {
  /**
   * Enable SDK logging for debugging
   * @default false
   */
  loggingEnabled?: boolean;

  /**
   * Custom container element to mount the modal
   * @default document.body
   */
  containerElement?: HTMLElement;

  /**
   * Z-index for the modal overlay
   * @default 9999
   */
  zIndex?: number;

  /**
   * Show close button on modal
   * @default true
   */
  showCloseButton?: boolean;

  /**
   * Show exit confirmation dialog when closing
   * @default true
   */
  showExitConfirmation?: boolean;

  /**
   * Automatically close modal when verification completes
   * @default false
   */
  closeModalOnComplete?: boolean;

  /**
   * Render verification inline instead of modal overlay
   * @default false
   */
  embedded?: boolean;

  /**
   * Container element ID for embedded mode
   * Required when embedded is true
   */
  embeddedContainerId?: string;
}

// Session + error types
type VerificationStatus = 'Approved' | 'Pending' | 'Declined';

interface SessionData {
  sessionId: string;
  status: VerificationStatus;
}

type VerificationErrorType =
  | 'sessionExpired'
  | 'networkError'
  | 'cameraAccessDenied'
  | 'unknown';

interface VerificationError {
  type: VerificationErrorType;
  message: string;
}

// Verification result — `session` and `error` are both optional fields
// on every result; which one is populated depends on `type`.
type VerificationResultType = 'completed' | 'cancelled' | 'failed';

interface VerificationResult {
  type: VerificationResultType;
  session?: SessionData;
  error?: VerificationError;
}

// State types
type DiditSdkState = 'idle' | 'loading' | 'ready' | 'error';

// Event types
type VerificationEventType =
  | 'didit:ready'
  | 'didit:started'
  | 'didit:step_started'
  | 'didit:step_completed'
  | 'didit:step_changed'
  | 'didit:media_started'
  | 'didit:media_captured'
  | 'didit:document_selected'
  | 'didit:verification_submitted'
  | 'didit:code_sent'
  | 'didit:code_verified'
  | 'didit:status_updated'
  | 'didit:completed'
  | 'didit:cancelled'
  | 'didit:error'
  | 'didit:close_request';

interface VerificationEventData {
  sessionId?: string;
  status?: string;
  step?: string;
  nextStep?: string;
  previousStep?: string;
  error?: string;
  country?: string;
  documentType?: string;
  isAuto?: boolean;
  mediaType?: string;
  channel?: string;
  codeSize?: number;
  [key: string]: unknown;
}

interface VerificationEvent {
  type: VerificationEventType;
  data?: VerificationEventData;
  timestamp?: number;
}

// Start verification options
interface StartVerificationOptions {
  url: string;
  configuration?: DiditSdkConfiguration;
}
```
