> ## 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.

# 자바스크립트 신분증 인증 SDK: 웹 KYC

> KYC, KYB, AML, 라이브니스 감지를 위한 Didit JavaScript SDK. 임베디드, 리다이렉트, iframe 방식 지원. 전체 KYC 건당 $0.33부터, 월 $10 무료, 계약 불필요.



JavaScript SDK 연동 프롬프트
        프롬프트를 클립보드에 복사



      
      

\<pre>

\`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`

\</pre>



      
      

붙여넣을 곳

Didit JavaScript SDK는 사용자 경험을 완전히 제어하면서 웹 애플리케이션에 인증을 프로그래밍 방식으로 연동할 수 있는 방법을 제공합니다.***## 주요 기능- **간단한 API** - 싱글턴 패턴과 사용하기 쉬운 메서드
- **유연한 연동** - UniLink URL을 직접 사용하거나 백엔드를 통해 세션을 생성
- **반응형** - 데스크톱과 모바일 브라우저에서 모두 작동
- **사용자 지정 가능** - 스타일과 동작을 위한 구성 옵션
- **다양한 형식** - ESM, CommonJS, UMD 빌드 제공
- **TypeScript 지원** - 전체 타입 정의 포함
- **모달 및 인라인 모드** - 모달 오버레이 또는 인라인 삽입 중에서 선택
- **이벤트 콜백** - 인증 진행 상황(시작됨, 단계 완료, 완료됨)에 대한 실시간 이벤트
- **상태 관리** - 사용자 지정 UI를 위해 SDK 상태를 관찰***## 설치### 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>
```> [!NOTE]
>
> 최신으로 게시된 npm 릴리스는 `0.5.1`입니다.
> `0.5.0`에서 `DiditSdk.shared.submitEvent()`가 추가되었습니다([이벤트 제출](#이벤트-제출) 참조). `0.5.1`에서는 호스팅 플로우가 SDK 안에서 디지털 ID 지갑에 사용자의 ID를 요청할 수 있으며, 실패한 실행은 `errorMessage`와 함께 `error` 상태로 유지됩니다.
> `0.4.0`에서 네이티브 SDK에 이미 있던 카메라 옵션이 추가되었으며, 여기에는 얼굴 촬영을 후면 카메라로 여는 `defaultLivenessCamera`도 포함됩니다([카메라 옵션](#카메라-옵션) 참조).### 설치된 버전 확인설치된 릴리스에 대해서는 패키지 관리자가 여전히 기준입니다.```bash theme={null}
npm list @didit-protocol/sdk-web
npm view @didit-protocol/sdk-web version
```런타임 내보내기를 확인할 수도 있습니다.```typescript theme={null}
const { SDK_VERSION } = await import('@didit-protocol/sdk-web');

console.log(SDK_VERSION);
````0.3.1`부터 이 내보내기는 모든 JavaScript 형식(ESM, CommonJS, UMD, 압축된 UMD)과 TypeScript 선언에서 패키지 버전을 기반으로 생성되므로 설치된 릴리스와 항상 일치합니다.`0.3.0` 이하 버전은 설치된 버전과 관계없이 이 내보내기에서 `0.2.1`을 보고합니다. 이 버전들에서는 `npm list` 또는 잠금 파일을 사용하십시오.***## 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>
```***## 프레임워크 예제> [card] [SDK 웹 예제](https://github.com/didit-protocol/sdk-web-examples)
>
> React, Angular, Svelte, Next.js, Nuxt, Vue 등 여러 프레임워크에 대한 예제와 문서를 제공합니다.***## 연동 방법SDK를 연동하는 방법은 두 가지입니다.### 방법 1: UniLink URL (가장 간단함)Didit Console에서 워크플로의 UniLink URL을 직접 사용합니다. 백엔드가 필요하지 않습니다.```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'
});
```[Didit Console](https://business.didit.me) → 내 워크플로 → 링크 복사에서 UniLink URL을 가져오십시오.**생성된 session\_id는 이벤트를 통해 전달됩니다.**
**[이벤트 참조 보기](#이벤트-참조)**### 방법 2: 백엔드 세션 (프로덕션에 권장됨)백엔드가 Didit API를 통해 세션을 생성하고 호스팅 인증 URL을 반환합니다. 이 방식에서는 `vendor_data`, 세션별 `callback`, `metadata`를 직접 제어할 수 있으며, 사용자가 프런트엔드에 도달하기 전에 세션을 미리 생성할 수 있습니다.자세한 내용은 [Create Session API 참조](https://docs.didit.me/ko/sessions-api/create-session.md)를 확인하십시오.**백엔드 (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 });
});
```**프런트엔드**```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]
>
> 세션 생성 응답의 필드 이름은 `url`입니다([Create Session OpenAPI 스키마](https://docs.didit.me/ko/sessions-api/create-session.md) 참조). 이 값을 `startVerification({ url })`에 그대로 전달하십시오.***## 구성| 옵션 | 타입 | 기본값 | 설명 |
| - | - | - | - |
| `loggingEnabled` | `boolean` | `false` | 디버깅을 위해 SDK 로깅을 활성화합니다 |
| `containerElement` | `HTMLElement` | `document.body` | 모달을 마운트할 사용자 지정 컨테이너 요소입니다 |
| `zIndex` | `number` | `9999` | 모달 오버레이의 z-index입니다 |
| `showCloseButton` | `boolean` | `true` | 모달에 닫기 버튼을 표시합니다 |
| `showExitConfirmation` | `boolean` | `true` | 닫을 때 종료 확인 대화상자를 표시합니다 |
| `closeModalOnComplete` | `boolean` | `false` | 인증이 완료되면 모달을 자동으로 닫습니다 |
| `embedded` | `boolean` | `false` | 모달 오버레이 대신 인증을 인라인으로 렌더링합니다 |
| `embeddedContainerId` | `string` | — | 임베디드 모드의 컨테이너 요소 ID입니다(`embedded`가 `true`일 때 필수) |
| `defaultDocumentCamera` | `'front' \| 'back'` | `'back'` | 문서 촬영을 처음 시작할 때 열리는 카메라입니다. [카메라 옵션](#카메라-옵션) 참조 |
| `defaultLivenessCamera` | `'front' \| 'back'` | `'front'` | 얼굴(라이브니스) 촬영을 처음 시작할 때 열리는 카메라입니다. [카메라 옵션](#카메라-옵션) 참조 |
| `showDocumentCameraSwitchButton` | `boolean` | `true` | 문서 단계에서 촬영 중 카메라 전환 버튼을 표시합니다 |
| `showLivenessCameraSwitchButton` | `boolean` | `true` | 라이브니스 단계에서 촬영 중 카메라 전환 버튼을 표시합니다 |> [!TIP]
>
> 전체 `DiditSdkConfiguration` 인터페이스는 아래의 [TypeScript 타입 정의](#typescript-타입)를 참조하십시오.***## 카메라 옵션> [!NOTE]
>
> 카메라 옵션은 `@didit-protocol/sdk-web`**0.4.0** 이상 버전이 필요합니다.웹 SDK는 [네이티브 SDK](https://docs.didit.me/ko/integration/native-sdks/overview.md)와 동일한 네 가지 카메라 옵션을 제공합니다. 기본적으로 문서 촬영은 후면 카메라를, 얼굴(라이브니스) 촬영은 전면(셀피) 카메라를 엽니다. 두 단계 모두 카메라가 둘 이상인 기기에서는 촬영 화면 안에서 카메라를 전환할 수 있는 버튼이 표시됩니다. 키오스크처럼 얼굴 촬영을 후면 카메라로 열어야 하거나, 운영자가 기기를 들고 인증 대상자를 비추는 경우에는 `defaultLivenessCamera: 'back'`을 설정하세요.| 옵션 | 타입 | 기본값 | 설명 |
| - | - | - | - |
| `defaultDocumentCamera` | `'front' \| 'back'` | `'back'` | 문서 촬영 화면에 처음 들어갈 때 사용하는 렌즈 |
| `defaultLivenessCamera` | `'front' \| 'back'` | `'front'` | 라이브니스(수동형 얼굴) 촬영 화면에 처음 들어갈 때 사용하는 렌즈 |
| `showDocumentCameraSwitchButton` | `boolean` | `true` | 문서 화면에 촬영 중 카메라 전환 버튼을 표시합니다. `false`이면 사용자는 `defaultDocumentCamera`에 고정됩니다 |
| `showLivenessCameraSwitchButton` | `boolean` | `true` | 라이브니스 화면에 촬영 중 카메라 전환 버튼을 표시합니다. `false`이면 사용자는 `defaultLivenessCamera`에 고정됩니다 |```typescript theme={null}
// A kiosk: the operator points the device at the person, on the rear camera, with no way to switch
DiditSdk.shared.startVerification({
  url,
  configuration: {
    defaultLivenessCamera: 'back',
    showLivenessCameraSwitchButton: false
  }
});
```- 허용되는 렌즈 값은 `'front'`와 `'back'`뿐입니다. 그 밖의 값은 무시되며, boolean이 아닌 전환 버튼 플래그도 무시됩니다.
- 요청한 카메라가 없는 기기는 가지고 있는 카메라를 그대로 사용합니다. 예를 들어 `'back'`을 요청한 노트북은 웹캠을 계속 사용합니다. 카메라가 하나뿐인 기기에서는 플래그 값과 관계없이 전환 버튼이 표시되지 않습니다.
- 라이브니스 옵션은 수동형 라이브니스 검사에만 적용됩니다. 능동형 라이브니스에는 영향을 주지 않습니다.
- SDK는 이 옵션을 인증 URL의 쿼리 파라미터인 `document_camera`, `liveness_camera`, `document_camera_switch`, `liveness_camera_switch`로 인증 페이지에 전달합니다. SDK를 거치지 않고 인증 URL을 직접 열 경우에는 직접 설정해야 합니다. 쿼리 문자열이 없는 URL에는 `?liveness_camera=back`을, 이미 파라미터가 있는 URL에는 `&liveness_camera=back`을 덧붙이세요. 두 경우를 모두 처리하려면 `URL` API로 설정할 수 있습니다:```typescript theme={null}
const verificationUrl = new URL(url);
verificationUrl.searchParams.set('liveness_camera', 'back');
verificationUrl.searchParams.set('liveness_camera_switch', 'false');
window.location.assign(verificationUrl.toString());
```***## 이벤트 제출`DiditSdk.shared.submitEvent()`는 최종 사용자 이벤트(가입, 로그인, 로그인 실패, 로그아웃, 비밀번호 재설정, MFA, 기기 변경)를 Didit의 이벤트 모니터링으로 전송하기 위해 대기열에 넣습니다.
백엔드에서 해당 사용자의 모니터링 토큰을 발급하면, SDK가 기기 핑거프린트와 함께 이벤트를 일괄 전송합니다.
API 키는 절대 브라우저에 노출하지 마세요.```typescript theme={null}
DiditSdk.shared.submitEvent({
  monitoringToken, // minted by your backend for this person
  event: { category: "auth", actionType: "login", payload: { method: "password" } },
});
```토큰, 이벤트 필드, 전송 방식에 대한 내용은 [SDK 이벤트 제출](https://docs.didit.me/ko/transaction-monitoring/sdk-event-submission.md)을 참고하세요.***## 모달 크기 조정 및 스크롤> [!NOTE]
>
> 아래 크기 조정 동작은 **0.3.1** 이상 버전이 필요합니다. **0.3.0** 이하 버전에서는 짧은 데스크톱 및 가로 방향 뷰포트에서 인증 흐름이 잘릴 수 있습니다.모달 모드의 최대 너비는 500px, 최대 높이는 700px입니다. 높이가 낮은 데스크톱 및 가로 방향 뷰포트에서는 iframe이 보이는 뷰포트의 90%를 차지합니다. 인증 흐름은 iframe 내부에서 스크롤되므로, 모달 뒤의 페이지가 잠겨 있어도 모든 단계에 접근할 수 있습니다.SDK는 동적 뷰포트 단위를 지원하는 브라우저에서는 `dvh`를 사용하고, 이를 지원하지 않는 구형 브라우저에서는 `vh`로 대체합니다. 너비가 540px 이하이면 모달이 보이는 뷰포트 전체를 채웁니다. 닫기 버튼과 종료 확인 창은 모달 안에 계속 표시됩니다.### 새로 디자인된 인증 흐름> [!NOTE]
>
> 이 섹션의 내용은 아직 배포되지 않은 **0.5.0** 버전부터 적용됩니다. 업그레이드하기 전까지 모달은 위에서 설명한 크기를 유지합니다.0.5.0 버전부터는 페이지가 [`didit:layout` 메시지](https://docs.didit.me/ko/integration/web-sdks/incontext-iframe#diditlayout으로-iframe-크기-조정)로 레이아웃을 보고하면, 모달이 새로 디자인된 인증 흐름에 맞춰 크기를 조정합니다.| 창 너비 | 모달(0.5.0 버전부터) |
| - | - |
| 1024px 이상 | 모든 단계에서 960px × 660px이며, 창 너비와 높이의 최대 90%를 넘지 않습니다 |
| 541px에서 1023px | 너비 500px이며, 높이는 페이지 콘텐츠의 높이만큼이고, 창 높이의 최대 90%를 넘지 않습니다 |
| 540px 이하 | 전체 화면 |- **초기 크기(0.5.0 버전부터):** 페이지가 레이아웃을 보고하기 전까지, 그리고 레이아웃을 보고하지 않는 페이지에서는 모달이 기존 최대 크기인 500px × 700px을 유지합니다. 짧은 창에도 맞게 조정되며, 540px 이하에서는 전체 화면이 됩니다.
- **닫기 버튼(0.5.0 버전부터):** 페이지 상단 바 위에 표시되는 지름 44px의 원형 버튼입니다. 페이지의 밝은 테마 또는 어두운 테마를 따르며, 오른쪽에서 왼쪽으로 쓰는 언어에서는 왼쪽으로 이동합니다.
- **임베디드 모드(0.5.0 버전부터):** 이전과 같이 호스트 요소를 계속 채웁니다([임베디드 모드](#임베디드-모드) 참고).
- **인증 URL(0.5.0 버전부터):** SDK가 `sdk_presentation`(`modal` 또는 `embedded`)을 추가하고, 모달에서는 `showCloseButton` 값에 따라 `sdk_close_button`(`true` 또는 `false`)도 추가합니다. 이를 통해 페이지가 자신이 어떻게 표시되는지 알 수 있습니다.
- **이벤트(0.5.0 버전부터):** SDK가 `didit:layout`을 직접 처리하며, 이 메시지를 `onEvent`로 전달하지 않습니다.별도로 설정할 것은 없으며, 기존 옵션도 모두 그대로 작동합니다.***## 임베디드 모드임베디드 모드는 모달의 500px × 700px 최대 크기 제한을 사용하지 않습니다. SDK는 호스트 요소의 콘텐츠 영역을 채우므로, 해당 요소에 명시적이거나 계산된 높이를 지정하세요:```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'
  }
});
```호스트에 계산된 높이가 없으면 임베디드 iframe에도 사용할 수 있는 높이가 생기지 않습니다. 레이아웃이 바뀌면 호스트 크기도 함께 조정하세요. SDK는 이후에도 호스트 영역을 자동으로 채웁니다.***## 인증 결과SDK는 세 가지 유형의 결과를 반환합니다:### 완료됨인증 플로우가 완료되었습니다(승인됨, 대기 중, 거부됨).```typescript theme={null}
{
  type: 'completed',
  session: {
    sessionId: 'session-uuid',
    status: 'Approved' | 'Pending' | 'Declined'
  }
}
```> [!NOTE]
>
> TypeScript 타입은 `'Approved' | 'Pending' | 'Declined'`로 선언되어 있지만, 이 값은 호스팅 플로우의 세션 상태에서 그대로 전달됩니다. `In Review`와 같은 다른 상태도 받을 수 있습니다. `Pending`은 플로우가 상태를 보고하지 않았을 때 SDK가 사용하는 기본값입니다. `status`는 참고 정보로만 취급하고, 최종 결정은 [webhook](https://docs.didit.me/ko/integration/webhooks.md)을 기준으로 하세요.### 취소됨사용자가 인증 모달을 닫았습니다. `sessionId`를 이미 알고 있는 경우 `session`이 포함됩니다(iframe이 세션 ID를 보고하기 전에 사용자가 취소한 경우에는 `undefined`일 수 있습니다).```typescript theme={null}
{
  type: 'cancelled',
  session: {
    sessionId: 'session-uuid',
    status: 'Pending'
  }
}
```### 실패인증 중 오류가 발생했습니다.```typescript theme={null}
{
  type: 'failed',
  error: {
    type: 'sessionExpired' | 'networkError' | 'cameraAccessDenied' | 'unknown',
    message: 'Your verification session has expired.'
  }
}
```***## 상태 관리커스텀 UI를 위해 SDK 상태를 관찰할 수 있습니다:```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 레퍼런스### DiditSdk.shared싱글톤 SDK 인스턴스입니다.### 메서드| 메서드 | 설명 |
| - | - |
| `startVerification(options)` | 인증 플로우를 시작합니다 |
| `submitEvent(options)` | 모니터링 토큰과 함께 최종 사용자 이벤트를 이벤트 모니터링 대기열에 추가합니다 |
| `close()` | 인증 모달을 프로그래밍 방식으로 닫습니다 |
| `destroy()` | SDK 인스턴스를 제거하고 정리합니다 |### 속성| 속성 | 타입 | 설명 |
| - | - | - |
| `state` | `DiditSdkState` | 현재 SDK 상태 |
| `configuration` | `DiditSdkConfiguration \| undefined` | 활성 구성(인증이 진행 중일 때 설정됨) |
| `isPresented` | `boolean` | 인증 모달이 열려 있는지 여부 |
| `errorMessage` | `string \| undefined` | 오류 메시지(상태가 `error`일 때 설정됨). 실행에 실패하면 다시 시작하기 전까지 상태는 `error`로 유지됩니다 |### 콜백| 콜백 | 매개변수 | 설명 |
| - | - | - |
| `onComplete` | `(result: VerificationResult)` | 인증이 끝나면 호출됩니다 |
| `onStateChange` | `(state: DiditSdkState, error?: string)` | 상태가 바뀔 때 호출됩니다 |
| `onEvent` | `(event: VerificationEvent)` | 세부 인증 이벤트가 발생할 때 호출됩니다 |***## 세부 이벤트`onEvent` 콜백으로 인증 진행 상황을 추적합니다:```typescript theme={null}
DiditSdk.shared.onEvent = (event) => {
  console.log('Event:', event.type, event.data);
};
```### 이벤트 참조| 이벤트 | 설명 | 데이터 payload |
| - | - | - |
| `didit:ready` | 페이지가 준비되었으며 sessionId를 사용할 수 있음 | `{ sessionId }` |
| `didit:started` | 사용자가 인증을 시작함 | `{ sessionId? }` |
| `didit:step_started` | 인증 단계가 시작됨 | `{ step, sessionId? }` |
| `didit:step_completed` | 단계가 성공적으로 완료됨 | `{ step, nextStep?, sessionId? }` |
| `didit:step_changed` | 현재 단계가 변경됨 | `{ step, previousStep?, sessionId? }` |
| `didit:media_started` | 카메라/미디어 캡처가 시작됨 | `{ step, mediaType }` |
| `didit:media_captured` | 사진/영상이 캡처됨 | `{ step, isAuto }` |
| `didit:document_selected` | 사용자가 문서 유형과 국가를 선택함 | `{ documentType, country }` |
| `didit:verification_submitted` | 처리를 위해 데이터가 제출됨 | `{ step, sessionId? }` |
| `didit:code_sent` | OTP 코드가 전송됨 | `{ step, channel?, codeSize?, sessionId? }` |
| `didit:code_verified` | OTP 코드가 확인됨 | `{ step, sessionId? }` |
| `didit:status_updated` | 세션 상태가 변경됨 | `{ status, previousStep?, sessionId? }` |
| `didit:completed` | 인증 흐름이 완료됨 | `{ sessionId?, status?, country?, documentType? }` |
| `didit:cancelled` | 사용자가 인증을 취소함 | `{ sessionId? }` |
| `didit:error` | 오류가 발생함 | `{ error: string, step?, sessionId? }` |
| `didit:close_request` | 사용자가 모달 닫기를 요청함 | (데이터 없음) |### 연결 끊김사용자의 연결이 끊어지면 인증 페이지는 단계를 로딩 상태로 남겨 두는 대신 "No internet connection" 화면을 표시합니다.
이 페이지는 백그라운드에서 계속 연결을 확인하며, Didit에 다시 연결되는 즉시 화면을 스스로 닫고 사용자는 있던 단계에서 이어서 진행합니다.
SDK의 모달이나 iframe 안에서는 이 화면에 **인증 종료** 버튼도 표시됩니다. 이 버튼을 누르면 페이지가 `error: "network_error"`가 담긴 `didit:error`를 보낸 다음 `didit:cancelled`를 보내고, SDK는 모달을 닫고 `type: "cancelled"`와 함께 `onComplete`를 호출합니다.버전 0.5.0부터 페이지의 `didit:layout` 메시지는 모달 크기를 조정하며 `onEvent`에는 전달되지 않습니다. 대신 자체 iframe으로 페이지를 임베드하는 경우에는 [`didit:layout`으로 iframe 크기 조정](https://docs.didit.me/ko/integration/web-sdks/incontext-iframe#diditlayout으로-iframe-크기-조정)을 참고하세요.### 단계 값`step` 필드는 다음 값 중 하나일 수 있습니다:- `document_selection` - 문서 유형 선택
- `document_front` - 문서 앞면
- `document_back` - 문서 뒷면
- `face` - 얼굴/라이브니스 인증
- `email` - 이메일 인증
- `phone` - 전화번호 인증
- `poa` - 주소 증명
- `questionnaire` - 설문지 단계### 채널 값`code_sent`의 `channel` 필드는 다음 값일 수 있습니다:- `email` - 이메일로 코드 전송
- `sms` - SMS로 코드 전송
- `whatsapp` - WhatsApp으로 코드 전송### 코드 길이`code_sent`의 `codeSize` 필드는 OTP 코드의 길이를 나타냅니다(예: 4자리 또는 6자리).***## TypeScript 타입```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;

  /**
   * Camera the document capture opens first.
   * @default 'back'
   */
  defaultDocumentCamera?: CameraLens;

  /**
   * Camera the face (liveness) capture opens first.
   * Applies to passive liveness; devices without a rear camera keep the front one.
   * @default 'front'
   */
  defaultLivenessCamera?: CameraLens;

  /**
   * Show the in-capture camera switcher on the document step.
   * false locks the user to defaultDocumentCamera.
   * @default true
   */
  showDocumentCameraSwitchButton?: boolean;

  /**
   * Show the in-capture camera switcher on the liveness step.
   * false locks the user to defaultLivenessCamera.
   * @default true
   */
  showLivenessCameraSwitchButton?: boolean;
}

type CameraLens = 'front' | 'back';

// 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;
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.