## Overview

The SDK provides a fire-and-forget event tracking system for sending telemetry to the Incode dashboard. This allows you to track module lifecycle, screen transitions, and custom events throughout your verification flow.

Events are sent automatically by the SDK's built-in modules, but you can also track custom events from your own code. All events are:

- **Fire-and-forget**: Never blocks execution, never throws errors
- **Headless-compatible**: Available in `@incodetech/core` with zero UI dependencies
- **Auto-queued**: Events sent before SDK is configured are queued and sent automatically when `setup()` is called

## Quick Start

### TypeScript

```typescript
import {
  addEvent,
  moduleOpened,
  moduleClosed,
  eventModuleNames,
} from '@incodetech/core/events';

// Track module lifecycle
useEffect(() => {
  moduleOpened(eventModuleNames.phone);
  return () => moduleClosed(eventModuleNames.phone);
}, []);

// Track custom events
addEvent({
  code: 'customButtonClicked',
  module: eventModuleNames.phone,
  payload: { buttonId: 'submit' },
});
```

### Event Logging

Subscribe to all events for custom logging or analytics:

### TypeScript

```typescript
import { subscribeEvent } from '@incodetech/core/events';

// Custom logger - separate from API event sending
const unsubscribeLogger = subscribeEvent((event) => {
  console.log('[Event]', {
    code: event.code,
    module: event.module,
    screen: event.screen,
    payload: event.payload,
    timestamp: new Date(event.clientTimestamp ?? Date.now()).toISOString(),
  });
});

// Events are logged AND sent to API independently
addEvent({ code: 'test', module: 'PHONE' });
// → Logged to console via listener
// → Sent to API (automatic)

// Remove logger when done
unsubscribeLogger();
```

## API Reference

### Core Functions

#### `addEvent(event)`

Sends a single event to the dashboard. Fire-and-forget - never blocks or throws.

### TypeScript

```typescript
addEvent({
  code: 'captureAttemptFinished',
  module: eventModuleNames.id,
  screen: eventScreenNames.frontCameraCapture,
  payload: { logs: [], attemptNumber: 1 },
  clientTimestamp: Date.now(), // Optional, defaults to now
});
```

**Parameters:**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `code` | `string` | Yes | Event code identifier |
| `module` | `EventModule | string` | No | Module name (use `eventModuleNames` constants) |
| `screen` | `EventScreen | string` | No | Screen name (use `eventScreenNames` constants) |
| `payload` | `Record<string, unknown>` | No | Additional event data |
| `clientTimestamp` | `number` | No | Timestamp in milliseconds (defaults to `Date.now()`) |

#### `subscribeEvent(listener)`

Subscribes to all SDK events for logging, debugging, or custom analytics. Returns an unsubscribe function.

### TypeScript

```typescript
const unsubscribe = subscribeEvent((event) => {
  console.log('[Event]', event.code, event.module);
  // Your custom logic here
});

// Later
unsubscribe();
```

**Parameters:**

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `listener` | `(event: Event) => void` | Yes | Callback function that receives each event |

**Returns:** Unsubscribe function to remove the listener

**Note:** Subscribers receive events **before** they're queued or sent to the API. Listener errors are silently caught and don't affect event sending.

#### Helper Functions

### TypeScript

```typescript
// Module lifecycle
moduleOpened(module, screen?, payload?);
moduleClosed(module, screen?, payload?);

// Screen lifecycle
screenOpened(module, screen, payload?);
screenClosed(module, screen, payload?);

// Generic screen event
screenEvent({ module, screen, code, payload });
```

## Available Constants

### Module Names

### TypeScript

```typescript
import { eventModuleNames } from '@incodetech/core/events';
```

| Constant | Wire value |
| --- | --- |
| `eventModuleNames.selfie` | `'SELFIE'` |
| `eventModuleNames.authentication` | `'AUTHENTICATION'` |
| `eventModuleNames.authFace` | `'AUTH_FACE'` |
| `eventModuleNames.mlConsent` | `'ML_CONSENT'` |
| `eventModuleNames.combinedConsent` | `'COMBINED_CONSENT'` |
| `eventModuleNames.curpValidation` | `'CURP_VALIDATION'` |
| `eventModuleNames.faceMatch` | `'FACE_MATCH'` |
| `eventModuleNames.qr` | `'QR'` |
| `eventModuleNames.videoSelfie` | `'VIDEO_ONBOARDING'` |
| `eventModuleNames.id` | `'ID'` |
| `eventModuleNames.front` | `'ID'` (alias) |
| `eventModuleNames.back` | `'ID'` (alias) |
| `eventModuleNames.passport` | `'ID'` (alias) |
| `eventModuleNames.idOcr` | `'ID_OCR'` |
| `eventModuleNames.document` | `'DOCUMENT_CAPTURE'` |
| `eventModuleNames.creditCard` | `'CREDIT_CARD_FRONT'` |
| `eventModuleNames.creditCardBack` | `'CREDIT_CARD_BACK'` |
| `eventModuleNames.conference` | `'CONFERENCE'` |
| `eventModuleNames.otp` | `'OTP'` |
| `eventModuleNames.signature` | `'SIGNATURE'` |
| `eventModuleNames.ekyc` | `'EXTERNAL_VERIFICATION'` |
| `eventModuleNames.watchlist` | `'WATCHLIST'` |
| `eventModuleNames.globalWatchList` | `'GLOBAL_WATCHLIST'` |
| `eventModuleNames.customWatchList` | `'CUSTOM_WATCHLIST'` |
| `eventModuleNames.watchlistForBusiness` | `'WATCHLIST_FOR_BUSINESS'` |
| `eventModuleNames.email` | `'EMAIL'` |
| `eventModuleNames.phone` | `'PHONE'` |
| `eventModuleNames.instantVerifyEmail` | `'INSTANT_VERIFY_EMAIL'` |
| `eventModuleNames.instantVerifyConsent` | `'INSTANT_VERIFY_CONSENT'` |
| `eventModuleNames.forms` | `'FORMS'` |
| `eventModuleNames.customModule` | `'CUSTOM_MODULE'` |
| `eventModuleNames.customFields` | `'CUSTOM_FIELDS'` |
| `eventModuleNames.geolocation` | `'GEOLOCATION'` |
| `eventModuleNames.ekyb` | `'EKYB'` |
| `eventModuleNames.identityReuse` | `'IDENTITY_REUSE'` |
| `eventModuleNames.antifraud` | `'ANTIFRAUD'` |
| `eventModuleNames.aeSignature` | `'AE_SIGNATURE'` |
| `eventModuleNames.qeSignature` | `'QE_SIGNATURE'` |
| `eventModuleNames.crossDocumentDataMatch` | `'CROSS_DOCUMENT_DATA_MATCH'` |

### Screen Names

### TypeScript

```typescript
import { eventScreenNames } from '@incodetech/core/events';
```

Grouped by module for readability.

#### Selfie / Face Capture

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.faceCaptureTutorial` | `'SELFIE_CAPTURE_TUTORIAL'` |
| `eventScreenNames.faceCaptureCamera` | `'SELFIE_CAMERA_CAPTURE'` |
| `eventScreenNames.faceCaptureError` | `'SELFIE_ATTEMPT_FAILED'` |
| `eventScreenNames.faceCaptureSuccess` | `'SELFIE_UPLOAD_SUCCEEDED'` |
| `eventScreenNames.faceCaptureUpload` | `'SELFIE_UPLOAD_PROGRESS'` |
| `eventScreenNames.faceMatch` | `'FACE_MATCH'` |

#### Auth Face

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.authFace` | `'AUTH_FACE'` |
| `eventScreenNames.authFaceTutorial` | `'AUTH_FACE_TUTORIAL'` |
| `eventScreenNames.authFaceError` | `'AUTH_FACE_ATTEMPT_FAILED'` |
| `eventScreenNames.authFaceUpload` | `'AUTH_FACE_UPLOAD_PROGRESS'` |
| `eventScreenNames.authFaceUploadSuccess` | `'AUTH_FACE_UPLOAD_SUCCEEDED'` |
| `eventScreenNames.authFaceUploadFailed` | `'AUTH_FACE_UPLOAD_FAILED'` |

#### Consent

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.mlConsent` | `'MACHINE_LEARNING_CONSENT'` |
| `eventScreenNames.combinedConsent` | `'COMBINED_CONSENT'` |

#### CURP

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.curpEnter` | `'ENTER_CURP'` |
| `eventScreenNames.curpValidate` | `'VALIDATE_CURP'` |
| `eventScreenNames.curpGenerate` | `'GENERATE_CURP'` |
| `eventScreenNames.curpValidation` | `'CURP_VALIDATION_PROGRESS'` |
| `eventScreenNames.curpValidationFailed` | `'CURP_VALIDATION_FAILED'` |
| `eventScreenNames.curpValidationSuccess` | `'CURP_VALIDATION_SUCCEEDED'` |

#### QR

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.qrTutorial` | `'QR_TUTORIAL'` |
| `eventScreenNames.qrScan` | `'QR_SCAN'` |

#### OTP / Signature

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.smsOtp` | `'OTP'` |
| `eventScreenNames.simpleOTP` | `'OTP'` (alias) |
| `eventScreenNames.signatureInput` | `'SIGNATURE_INPUT'` |

#### ID — Front

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.frontTutorial` | `'FRONT_ID_CAPTURE_TUTORIAL'` |
| `eventScreenNames.frontCameraCapture` | `'FRONT_ID_CAMERA_CAPTURE'` |
| `eventScreenNames.frontHelp` | `'FRONT_ID_HELP'` |
| `eventScreenNames.frontReviewPhoto` | `'FRONT_ID_REVIEW_PHOTO'` |
| `eventScreenNames.frontUploadProgress` | `'FRONT_ID_UPLOAD_PROGRESS'` |
| `eventScreenNames.frontAttemptFailed` | `'FRONT_ID_ATTEMPT_FAILED'` |
| `eventScreenNames.frontUploadSuccess` | `'FRONT_ID_UPLOAD_SUCCEEDED'` |

#### ID — Back

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.backTutorial` | `'BACK_ID_CAPTURE_TUTORIAL'` |
| `eventScreenNames.backCameraCapture` | `'BACK_ID_CAMERA_CAPTURE'` |
| `eventScreenNames.backHelp` | `'BACK_ID_HELP'` |
| `eventScreenNames.backReviewPhoto` | `'BACK_ID_REVIEW_PHOTO'` |
| `eventScreenNames.backUploadProgress` | `'BACK_ID_UPLOAD_PROGRESS'` |
| `eventScreenNames.backAttemptFailed` | `'BACK_ID_ATTEMPT_FAILED'` |
| `eventScreenNames.backUploadSuccess` | `'BACK_ID_UPLOAD_SUCCEEDED'` |

#### Passport

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.passportTutorial` | `'TUTORIAL_PASSPORT'` |

#### Document Capture

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.documentTutorial` | `'DOCUMENT_CAPTURE_TUTORIAL'` |
| `eventScreenNames.documentCameraCapture` | `'DOCUMENT_CAMERA_CAPTURE'` |
| `eventScreenNames.documentHelp` | `'DOCUMENT_HELP'` |
| `eventScreenNames.documentReviewPhoto` | `'DOCUMENT_REVIEW_PHOTO'` |
| `eventScreenNames.documentUploadProgress` | `'DOCUMENT_UPLOAD_IN_PROGRESS'` |
| `eventScreenNames.documentAttemptFailed` | `'DOCUMENT_ATTEMPT_FAILED'` |
| `eventScreenNames.documentUploadSuccess` | `'DOCUMENT_UPLOAD_SUCCEEDED'` |

#### Digital ID

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.digitalIdFileSelection` | `'DIGITAL_ID_FILE_SELECTION'` |
| `eventScreenNames.digitalIdFileReview` | `'DIGITAL_ID_FILE_REVIEW'` |
| `eventScreenNames.digitalIdUploadProgress` | `'DIGITAL_ID_UPLOAD_PROGRESS'` |
| `eventScreenNames.digitalIdUploadSuccess` | `'DIGITAL_ID_UPLOAD_SUCCEEDED'` |
| `eventScreenNames.digitalIdUploadFailed` | `'DIGITAL_ID_UPLOAD_FAILED'` |
| `eventScreenNames.digitalIdAnalysisProgress` | `'DIGITAL_ID_ANALYSIS_PROGRESS'` |
| `eventScreenNames.digitalIdVerificationSuccess` | `'DIGITAL_ID_VERIFICATION_SUCCESS'` |
| `eventScreenNames.digitalIdVerificationFailed` | `'DIGITAL_ID_VERIFICATION_FAILED'` |

#### Conference

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.conferenceWait` | `'CONFERENCE_WAIT'` |
| `eventScreenNames.conferenceVideoChat` | `'CONFERENCE_VIDEO_CHAT'` |
| `eventScreenNames.conferenceMessageChat` | `'CONFERENCE_MESSAGE_CHAT'` |

#### Video Selfie

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.videoSelfieTutorial` | `'VIDEO_SELFIE_TUTORIAL'` |
| `eventScreenNames.videoSelfie` | `'VIDEO_SELFIE'` |
| `eventScreenNames.videoSelfieFaceCapture` | `'VIDEO_SELFIE_FACE_CAPTURE'` |
| `eventScreenNames.videoSelfieFaceUploadProgress` | `'VIDEO_SELFIE_FACE_UPLOAD_PROGRESS'` |
| `eventScreenNames.videoSelfieFaceAttemptFailed` | `'VIDEO_SELFIE_FACE_ATTEMPT_FAILED'` |
| `eventScreenNames.videoSelfieFaceUploadSucceeded` | `'VIDEO_SELFIE_FACE_UPLOAD_SUCCEEDED'` |
| `eventScreenNames.videoSelfieFrontIdCapture` | `'VIDEO_SELFIE_FRONT_ID_CAPTURE'` |
| `eventScreenNames.videoSelfieFrontIdUploadProgress` | `'VIDEO_SELFIE_FRONT_ID_UPLOAD_PROGRESS'` |
| `eventScreenNames.videoSelfieFrontIdAttemptFailed` | `'VIDEO_SELFIE_FRONT_ID_ATTEMPT_FAILED'` |
| `eventScreenNames.videoSelfieFrontIdUploadSucceeded` | `'VIDEO_SELFIE_FRONT_ID_UPLOAD_SUCCEEDED'` |
| `eventScreenNames.videoSelfieBackIdCapture` | `'VIDEO_SELFIE_BACK_ID_CAPTURE'` |
| `eventScreenNames.videoSelfieBackIdUploadProgress` | `'VIDEO_SELFIE_BACK_ID_UPLOAD_PROGRESS'` |
| `eventScreenNames.videoSelfieBackIdAttemptFailed` | `'VIDEO_SELFIE_BACK_ID_ATTEMPT_FAILED'` |
| `eventScreenNames.videoSelfieBackIdUploadSucceeded` | `'VIDEO_SELFIE_BACK_ID_UPLOAD_SUCCEEDED'` |
| `eventScreenNames.videoSelfieDocumentCapture` | `'VIDEO_SELFIE_DOCUMENT_CAPTURE'` |
| `eventScreenNames.videoSelfieVoiceQuestion` | `'VIDEO_SELFIE_VOICE_QUESTION'` |
| `eventScreenNames.videoSelfieVoiceFinalQuestion` | `'VIDEO_SELFIE_VOICE_FINAL_QUESTION'` |
| `eventScreenNames.videoSelfieVideoUpload` | `'VIDEO SELFIE VIDEO UPLOAD'` |

#### eKYC / eKYB / Forms

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.ekycInput` | `'EKYC_INPUT'` |
| `eventScreenNames.ekycProgress` | `'EKYC_PROGRESS'` |
| `eventScreenNames.ekycSucceeded` | `'EKYC_SUCCEEDED'` |
| `eventScreenNames.ekycFailed` | `'EKYC_FAILED'` |
| `eventScreenNames.ekybForm` | `'EKYB_FORM'` |
| `eventScreenNames.forms` | `'FORMS'` |

#### Watchlists

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.globalWatchListInput` | `'GLOBAL_WATCHLIST_INPUT'` |
| `eventScreenNames.globalWatchListProgress` | `'GLOBAL_WATCHLIST_PROGRESS'` |
| `eventScreenNames.globalWatchListSuccess` | `'GLOBAL_WATCHLIST_SUCCEEDED'` |
| `eventScreenNames.globalWatchListFailed` | `'GLOBAL_WATCHLIST_FAILED'` |
| `eventScreenNames.customWatchListInput` | `'CUSTOM_WATCHLIST_INPUT'` |
| `eventScreenNames.customWatchListProgress` | `'CUSTOM_WATCHLIST_PROGRESS'` |
| `eventScreenNames.customWatchListSuccess` | `'CUSTOM_WATCHLIST_SUCCEEDED'` |
| `eventScreenNames.customWatchListFailed` | `'CUSTOM_WATCHLIST_FAILED'` |

#### Phone / Email / Instant Verify

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.phoneInput` | `'PHONE_INPUT'` |
| `eventScreenNames.emailInput` | `'EMAIL_INPUT'` |
| `eventScreenNames.instantVerify` | `'INSTANT_VERIFY'` |

#### Custom Module

| Constant | Wire value |
| --- | --- |
| `eventScreenNames.customModuleCallback` | `'CUSTOM_MODULE_CALLBACK'` |
| `eventScreenNames.customModuleProcessing` | `'CUSTOM_MODULE_PROCESSING'` |

### Video Selfie Events

### TypeScript

```typescript
import { videoSelfieEvents } from '@incodetech/core/events';
```

| Constant | Wire value |
| --- | --- |
| `videoSelfieEvents.imageTooBlurry` | `'imageTooBlurry'` |
| `videoSelfieEvents.fillFrame` | `'fillFrame'` |
| `videoSelfieEvents.checkCameraOrLighting` | `'checkCameraOrLighting'` |
| `videoSelfieEvents.frontIdCaptureStarted` | `'frontIdCaptureStarted'` |
| `videoSelfieEvents.lookAtCamera` | `'lookAtCamera'` |
| `videoSelfieEvents.moveCloser` | `'moveCloser'` |
| `videoSelfieEvents.lookingForFrontId` | `'lookingForFrontId'` |
| `videoSelfieEvents.lookingForBackId` | `'lookingForBackId'` |
| `videoSelfieEvents.cameraSwitchToBack` | `'cameraSwitchToBack'` |
| `videoSelfieEvents.cameraSwitchToFront` | `'cameraSwitchToFront'` |
| `videoSelfieEvents.audioStreamOpened` | `'audioStreamOpened'` |
| `videoSelfieEvents.audioStreamClosed` | `'audioStreamClosed'` |
| `videoSelfieEvents.videoSelfieVideoUploadInProgress` | `'videoSelfieVideoUploadInProgress'` |
| `videoSelfieEvents.videoSelfieTosNotAccepted` | `'videoSelfieTosNotAccepted'` |
| `videoSelfieEvents.videoSelfieTosAccepted` | `'videoSelfieTosAccepted'` |
| `videoSelfieEvents.videoSelfieStreamCreated` | `'videoSelfieStreamCreated'` |
| `videoSelfieEvents.videoSelfieStreamDestroyed` | `'videoSelfieStreamDestroyed'` |
| `videoSelfieEvents.videoSelfieReconnecting` | `'videoSelfieReconnecting'` |
| `videoSelfieEvents.videoSelfieReconnected` | `'videoSelfieReconnected'` |

## Event Queue

Events sent before the SDK is configured (before `setup()` is called with a token) are automatically queued. When `setup()` is called with a token, all queued events are automatically flushed and sent.

### TypeScript

```typescript
// This event will be queued
addEvent({ code: 'sdkLoading', payload: { timestamp: Date.now() } });

// Later, after setup with token
await setup({ apiURL: '...', token: 'session-token' });
// ^ Queued events are automatically sent here
```

## Common Event Codes

The SDK automatically sends these events from built-in modules:

| Code | Description | When Sent |
| --- | --- | --- |
| `moduleOpened` | Module started | When module component mounts |
| `moduleClosed` | Module ended | When module component unmounts |
| `screenOpened` | Screen displayed | When screen component mounts |
| `screenClosed` | Screen hidden | When screen component unmounts |
| `continue` | User continued | When user clicks continue/submit |
| `tutorialSelfie` | Selfie tutorial shown | When selfie tutorial displays |
| `tutorialVideoStarted` | ID tutorial video started | When ID tutorial video plays |
| `captureAttemptFinished` | Capture attempt completed | After capture upload completes |
| `strSessionDidConnect` | Recording session connected | When video recording starts |
| `strStreamVideoCaptureStart` | Video capture started | When video recording begins |

## Examples

### Track Custom Button Click

### TypeScript

```typescript
import { addEvent, eventModuleNames } from '@incodetech/core/events';

function handleSubmit() {
  addEvent({
    code: 'customSubmitClicked',
    module: eventModuleNames.phone,
    payload: { timestamp: Date.now() },
  });

// Your submit logic...
}
```

### Track Screen Transition

### TypeScript

```typescript
import {
  screenEvent,
  eventModuleNames,
  eventScreenNames,
} from '@incodetech/core/events';

function navigateToTutorial() {
  screenEvent({
    module: eventModuleNames.id,
    screen: eventScreenNames.frontTutorial,
    code: 'screenOpened',
  });

// Navigate...
}
```

### Track Module Lifecycle in Headless Mode

### TypeScript

```typescript
import {
  moduleOpened,
  moduleClosed,
  eventModuleNames,
} from '@incodetech/core/events';

function MyCustomPhoneFlow() {
  useEffect(() => {
    moduleOpened(eventModuleNames.phone);

return () => {
      moduleClosed(eventModuleNames.phone);
    };
  }, []);

// Your custom implementation...
}
```

## Best Practices

1. **Use constants** \- Always use `eventModuleNames` and `eventScreenNames` instead of hardcoded strings
2. **Fire-and-forget** \- Don't await `addEvent()` \- it's designed to never block
3. **Use hooks for lifecycle** \- Use `useModuleEvents`/`useScreenEvents` for automatic tracking
4. **Include context** \- Add relevant data in `payload` for debugging
5. **Don't track sensitive data** \- Never include PII or sensitive information in event payloads
6. **Use subscribers for custom logging** \- Use `subscribeEvent()` for custom analytics or logging needs
7. **Keep listeners lightweight** \- Event listeners are called synchronously; avoid heavy operations
