npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@beexpert.ai/proctor

v2.2.0

Published

BeExpert Proctoring SDK.

Readme

BeExpert Proctoring Client Guide

This guide is for client product and engineering teams integrating BeExpert Proctoring into assessment, interview, and certification flows.

Step 1: Install the Package

Install the SDK in your frontend project:

# Production registry
npm install @beexpert.ai/proctor

# OR local tarball
npm install ./beexpert.ai-proctor-<version>.tgz

For React apps, use the React integration:

import { useProctor } from '@beexpert.ai/proctor/react';

Step 2: Provision a Session from Your Backend

Do NOT call the proctoring SDK first. Your frontend should call your own backend to provision a session.

2.1 Your Backend Calls Proctoring API

Your backend should provision the session with BeExpert API:

API call format:

POST /api/v1/proctoringSessions HTTP/1.1
Host: apiBaseUrl
Content-Type: application/json
x-api-key: <your-api-key>
x-tenant-id: <your-tenant-id>
x-user-id: SYSTEM

{
  "EvaluationOn": {
    "Type": "Assessment", // proctoring works on different Type but configured only for Assessment
    "On_Id": "CHEM-NEET-2026", // Assessment On record Key like  CHEMISTRY NEET (i.e. Name or unique identifier for the assessment) 
    "title": 'Chemistry NEET 2026 Assessment', // friendly title
    scheduledStartAt: new Date().toISOString(),/ start of assessment
    scheduledDurationSeconds: 900,// remaining time of assessment
  },
  "candidate": {
    "candidate_id": "STU-10293",  // candidate Key
    "candidate_name": "Rahul Sharma",
    "candidate_mobile": "1234567890",
    "candidate_email": "[email protected]"
  }
}

Curl example:

curl -X POST "https://apiBaseUrl/api/v1/proctoringSessions" \
  -H "Content-Type: application/json" \
  -H "x-api-key: <your-api-key>" \
  -H "x-tenant-id: <your-tenant-id>" \ 
  -H "x-user-id: SYSTEM" \
  -d '{
    "EvaluationOn": {
      "Type": "Assessment",
      "On_Id": "CHEM-NEET-2026",
      "title": "Chemistry NEET 2026 Assessment",
      "scheduledDurationSeconds": 900,
      "scheduledStartAt": "2026-07-03T06:19:10.678Z"
    },
    "candidate": {
      "candidate_id": "STU-10293",
      "candidate_name": "Rahul Sharma",
      "candidate_mobile": "1234567890",
      "candidate_email": "[email protected]"
    }
  }'

Response from Backend:

{
  "session": {
    "id": "session-uuid-here"
  },
  "tokens": {
    "accessToken": "v4.public.eyJ...",
    "accessTokenTTL": 960
  }
}

2.2 Backend Returns Bootstrap Payload to Frontend

Your backend returns session credentials to the frontend:

{
  sessionId: 'session-uuid-here',
  sessionToken: 'v4.public.eyJ...',
  accessTokenTTL: 960,
  apiUrl: 'https://apiBaseUrl/api/v1',
  scheduledStartAt: '2026-07-02T14:30:00Z',
  scheduledDurationSeconds: 900,
  title: 'Resume Screening Test'
}

Key Points:

  • sessionId: Unique identifier for this assessment attempt
  • sessionToken: JWT valid until (assessmentStart + assessmentDuration + 60 seconds)
  • accessTokenTTL: Token validity duration in seconds
  • scheduledStartAt: When the assessment is scheduled to begin (ISO 8601 timestamp)
  • scheduledDurationSeconds: Total assessment time in seconds
  • apiUrl: Backend proctoring API base URL including /api/v1 (use https://apiBaseUrl/api/v1)

Step 3: Initialize the SDK with Session Credentials

3.1 Store Credentials in Session Storage

Once you receive the bootstrap payload, store it for the assessment page:

const sessionBootstrap = await response.json();

sessionStorage.setItem('bx_sessionId', sessionBootstrap.sessionId);
sessionStorage.setItem('bx_token', sessionBootstrap.sessionToken);
sessionStorage.setItem('bx_tokenExpirySeconds', String(sessionBootstrap.accessTokenTTL + Math.floor(Date.now() / 1000)));
sessionStorage.setItem('bx_title', sessionBootstrap.title);
sessionStorage.setItem('bx_scheduledStartAt', sessionBootstrap.scheduledStartAt);
sessionStorage.setItem('bx_scheduledDurationSeconds', String(sessionBootstrap.scheduledDurationSeconds));
sessionStorage.setItem('bx_apiBaseUrl', sessionBootstrap.apiUrl);

router.push('/assessment');

3.2 Retrieve and Pass to useProctor

On your assessment page, retrieve credentials and initialize the SDK:

import { useProctor } from '@beexpert.ai/proctor/react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';

export default function AssessmentPage() {
  const [credentials, setCredentials] = useState(null);

  useEffect(() => {
    const sessionId = sessionStorage.getItem('bx_sessionId');
    const sessionToken = sessionStorage.getItem('bx_token');
    const apiUrl = sessionStorage.getItem('bx_apiBaseUrl');

    if (!sessionId || !sessionToken) {
      router.push('/'); // Redirect if no valid session
      return;
    }

    setCredentials({ sessionId, sessionToken, apiUrl });
  }, []);

  // Initialize SDK with session credentials
  const { start, stop, status, on } = useProctor({
    sessionId: credentials?.sessionId,
    sessionToken: credentials?.sessionToken,
    apiUrl: credentials?.apiUrl,
    autoStart: false, // User must click "Start" button
    ui: {
      enabled: true,
      showProctoringControls: true,
      toastDurationMs: 4000,
      showWarningBanner: true, // to enable topbar message
      showWarningToast: true // to enable alongside message

    },
    logLevel: 'debug'
  });

  const handleStartAssessment = async () => {
    await start();
  };

  const handleStopAssessment = async () => {
    await stop('CANDIDATE_ENDED');
  };

  return (
    <div>
      <button onClick={handleStartAssessment} disabled={status === 'running'}>
        Start Proctored Assessment
      </button>
      <button onClick={handleStopAssessment} disabled={status !== 'running'}>
        End Assessment
      </button>
      <div>Status: {status}</div>
    </div>
  );
}

Step 4: Resume Assessment on Page Refresh

If the user refreshes the page or navigates away, you can resume the same session using the same token:

// On page refresh/reload
const sessionId = sessionStorage.getItem('bx_sessionId');
const sessionToken = sessionStorage.getItem('bx_token');
const apiUrl = sessionStorage.getItem('bx_apiBaseUrl');

if (sessionId && sessionToken) {
  // Resume same session - SDK continues with the same token
  const { start, stop, status } = useProctor({
    sessionId,
    sessionToken,
    apiUrl,
    autoStart: false
  });

  // SDK will resume from where it left off
  await start();
}

Important:

  • The same sessionToken is valid for the entire assessment duration
  • No token refresh needed - token is valid until assessment ends + 60 seconds
  • Store sessionId and sessionToken in session storage to survive page refresh
  • If page is refreshed, use the exact same credentials to resume

Step 5: Understanding Assessment Timing

5.1 Scheduled Start Time vs. Actual Start Time

The assessment has a scheduled window, but the candidate may start late:

Scenario: Assessment window is 2:00 PM - 5:00 PM (3 hours)
Candidate joins at: 2:30 PM

scheduledStartAt:      2:00 PM (original window start)
scheduledDurationSeconds: 10,800 (3 hours = 180 minutes)

BUT the candidate only has time from 2:30 PM onward.
Total remaining time: 2.5 hours = 9,000 seconds

5.2 Backend Calculates Actual Assessment Time

Your backend should provision the session with actual timing:

// Backend logic when candidate joins at 2:30 PM
const windowStart = new Date('2026-07-02T14:00:00Z');
const windowEnd = new Date('2026-07-02T17:00:00Z');
const candidateStartTime = new Date('2026-07-02T14:30:00Z');

// For provisioning, use actual times
const scheduledStartAt = Actual time; // 2:00 PM
const actualDuration = (windowEnd - candidateStartTime) / 1000; // 9,000 seconds

// Token valid until: assessmentStart + assessmentDuration + 60 seconds
// 2:30 PM + 2.5 hours + 1 min = 5:01 PM

5.3 Token Expiry Calculation

The SDK calculates token expiry as on start proctoring:

Token Expiry = candiateStartTime + scheduledDurationSeconds + 60 seconds

Example:

  • Proctoring Start: 2:30 PM
  • Duration: 2.5 hours (9,000 seconds)
  • Token Expires: 2:30 PM + 2.5 hours + 1 minute = 5:01 PM

If the candidate is still in the assessment at 5:01 PM, the token will be expired and the session will terminate.


6. Session Lifecycle and Status

The SDK lifecycle is:

  1. Initialize: useProctor() with credentials
  2. Start: await start() - requests permissions, starts monitors/transport
  3. Run: violations and warnings are evaluated continuously
  4. Stop: await stop(reason) - finalizes and ends session

Current status values you can receive on session:status:

  1. idle
  2. initializing
  3. ready
  4. running
  5. paused
  6. stopping
  7. terminated
  8. completed
  9. error

Recommended UI behavior:

  1. Show full-screen blocking spinner only during initializing.
  2. During stopping, disable Start/Stop buttons and show "Ending assessment...".
  3. Treat terminated and completed as terminal states.

7. Events to Handle (Important)

The SDK provides a comprehensive event bus that emits strongly-typed events during the assessment. You can listen to these events to drive your own custom UI or telemetry.

7.1 Lifecycle Events

// Session state transitions
on('session:status', ({ status, state }) => {
  console.log('Status changed:', status, state);
});

// Emitted when SDK has fetched config and is ready to start
on('session:ready', ({ state }) => {
  console.log('Proctoring ready to start');
});

// Emitted when session actually starts.
// CRITICAL: Save the new sessionToken to survive network calls!
on('session:started', ({ state, sessionToken }) => {
  console.log('Proctoring started');
  if (sessionToken) {
    sessionStorage.setItem('bx_token', sessionToken);
  }
});

// Terminal states
on('session:terminated', ({ reason, state, serverResponse }) => {
  console.log('Session forcibly terminated:', reason);
  // Note: Because this fires instantly to freeze the UI, serverResponse is undefined here.
  // To get the server's response for an abandoned session, await proctor.stop(reason).
});

on('session:completed', ({ state, summary, serverResponse }) => {
  console.log('Assessment naturally completed', summary);
  console.log('Server response:', serverResponse);
});

// Unrecoverable errors
on('session:error', ({ error }) => {
  console.error('Proctoring error:', error);
});

7.2 Violation & Detection Events

// Raw violation detected (before debouncing)
on('violation:raw', ({ violation }) => {
  console.log('Raw violation:', violation.type);
});

// Confirmed violation (after debouncing)
on('violation:confirmed', ({ violation }) => {
  console.log('Violation confirmed:', violation.type, violation.reason);
});

// Canonical backend event mapping (best for custom UI warnings)
on('detection', ({ type, severity, eventType, violation }) => {
  console.log('Detection Event:', eventType, type, severity);
});

// Accumulation of warnings (drives termination logic)
on('violation:warning', ({ violation, mediumCount, maxMedium, highCount, maxHigh }) => {
  console.log(`Warnings: Medium(${mediumCount}/${maxMedium}) High(${highCount}/${maxHigh})`);
});

// Server-aligned escalation (WARN, BLOCK, CANCEL)
on('violation:escalation', ({ action, eventType, occurrenceCount, windowSeconds, violation }) => {
  console.log(`Escalation: ${action} for ${eventType} (${occurrenceCount} times in ${windowSeconds}s)`);
});

7.3 Media, Transport, and UI Events

// Camera/Mic/Screen permission states
on('permission:request', ({ kind }) => console.log('Requesting:', kind));
on('permission:change', ({ kind, state }) => console.log('Permission changed:', kind, state));

// Media streams status
on('media:stream', ({ kind, active }) => console.log('Media:', kind, active));

// Recording events
on('recording:chunk', ({ index, size }) => console.log(`Recording slice ${index} captured (${size} bytes)`));
on('recording:chunk:uploaded', ({ index, chunk, url }) => {
  console.log(`Slice ${index} successfully uploaded to ${url}`);
  // You can stream the raw Blob (chunk) locally without re-downloading it
});

// Transport connectivity
on('transport:open', () => console.log('WebSocket open'));
on('transport:close', ({ code, reason }) => console.log('WebSocket closed:', code, reason));
on('transport:reconnecting', ({ attempt, delayMs }) => console.log(`Reconnecting: attempt ${attempt} in ${delayMs}ms`));

// Internal SDK Toasts & Dialogs
on('ui:toast', ({ message, severity, durationMs }) => console.log('Toast:', severity, message));
on('ui:dialog', ({ kind, payload }) => console.log('Dialog opened:', kind));

7.4 All Available Violation Types

The SDK detects a wide array of anomalies. These are categorized into AI/Vision, Audio, Browser, Media, and System violations:

AI / Vision:

  • NO_FACE
  • MULTIPLE_FACES
  • FACE_MISSING
  • LOOKING_AWAY
  • EYES_CLOSED
  • HEAD_POSE_OUT_OF_RANGE
  • FACE_TOO_FAR
  • MOBILE_PHONE_DETECTED
  • EXTRA_PERSON_DETECTED
  • VIRTUAL_CAMERA_DETECTED

Audio:

  • BACKGROUND_NOISE
  • MULTIPLE_VOICES
  • VOICE_CHANGED

Browser:

  • TAB_SWITCH
  • WINDOW_BLUR
  • FULLSCREEN_EXIT
  • DEVTOOLS_OPEN
  • COPY_DETECTED
  • PASTE_DETECTED
  • CONTEXT_MENU_BLOCKED

Media / System:

  • CAMERA_LOST
  • MICROPHONE_LOST
  • SCREEN_SHARE_STOPPED
  • SCREEN_CHANGE
  • PERMISSION_DENIED
  • NETWORK_DISCONNECT

7.5 Violation Event Meaning

  • violation:confirmed: a rule condition was confirmed by detector logic.
  • violation:warning: warning counters were updated.
  • violation:escalation: escalation action computed by engine (WARN, BLOCK, CANCEL).

confirmed does not always mean session ends. Session ends when threshold/escalation logic decides so.

7.6 Typical Event Order (Normal Candidate Stop)

  1. session:status -> running
  2. user clicks Stop
  3. session:status -> stopping
  4. server finalize (complete for candidate-ended, otherwise abandon)
  5. session:completed (candidate-ended flow)

7.7 Typical Event Order (Threshold Termination)

  1. repeated violation:confirmed
  2. violation:warning reaches threshold
  3. session:terminated with reason such as MAX_WARNINGS_EXCEEDED
  4. session:status reaches terminal state

For terminated flow, do not expect session:completed.

7.8 About 409 Near Session End

A single 409 around shutdown can occur if an in-flight request races with end-of-session. The SDK treats terminal statuses as shutdown signals and closes the loop. If termination/completion event already arrived, treat this as expected end noise.


8. What You Must Know

  • No Token Refresh: Token is valid for entire assessment. No refresh endpoint.
  • Session Storage: Store sessionId, sessionToken, and timing in session storage.
  • Tenant Isolation: SDK enforces tenant validation from token claims.
  • Assessment Timing: Backend must provision with actual candidate times, not window times.
  • Single Token: One JWT token valid from assessment start to assessment end + 60 seconds.
  • No Manual Expiry Handling: SDK automatically handles token expiry.
  • Terminal UX Rule: Once session:terminated or session:completed arrives, lock assessment UI and show end-state screen.

9. Fetching the Session Report

9.1 SDK Internal Session Completion

When the session is completed or abandoned gracefully by the SDK (e.g. candidate ends the session), the SDK internally makes a request to /complete.

  • Endpoint: POST /api/v1/proctoringSessions/:sessionId/complete
  • Headers: Authorization: Bearer <sessionToken>
  • Body: { "reason": "CANDIDATE_ENDED" }

The backend finalizes the session, triggers background server-side analysis if configured, and returns the Unified Session Report. Because this endpoint directly returns the final report, your client-side application can immediately use this response data to display a results summary (like an integrity score) right as the session concludes.

[!TIP] Implementation Best Practice: If your assessment runs on a timer, it is highly recommended to call proctor.stop() exactly 2-5 seconds before your assessment timer reaches zero. This gives the SDK the necessary time to flush the final local violations, request the /complete report from the server, and receive the calculated integrity score precisely as the assessment ends, resulting in a seamless UX without loading spinners.

9.2 Fetching the Report Later (Server-to-Server)

To retrieve the session report later (e.g., from your backend server to display to the examiner/reviewer), make a GET request using your API key.

Endpoint:

GET /api/v1/proctoringSessions/reports/:sessionId HTTP/1.1
Host: apiBaseUrl
x-api-key: <your-api-key>
x-tenant-id: <your-tenant-id>
x-user-id: SYSTEM

Curl Example:

curl -X GET "https://apiBaseUrl/api/v1/proctoringSessions/reports/session-uuid-here" \
  -H "x-api-key: <your-api-key>" \
  -H "x-tenant-id: <your-tenant-id>" \
  -H "x-user-id: SYSTEM"

9.3 Unified Report Response Schema

Both the SDK's complete call and the Server's report fetch return the exact same ProctoringSessionReportResponse schema.

Response Body (200 OK):

{
  "sessionId": "6a4c86ac0e6682736f8a55c0",
  "summary": {
    "candidateId": "STU-10293",
    "sessionStatus": "COMPLETED",
    "startedAt": "2026-07-02T14:30:00Z",
    "endedAt": "2026-07-02T15:00:00Z",
    "durationSeconds": 1800,
    "integrityScore": 85,
    "totalViolations": 2
  },
  "violations": [
    {
      "id": "6a4c86ac0e6682736f8a55c8",
      "violationType": "THRESHOLD_BREACH",
      "eventType": "Window Minimized",
      "capturedAt": "2026-07-07T04:55:06.882Z",
      "timestamp": "2026-07-07T04:55:06.882Z",
      "severity": "HIGH",
      "evidenceUrl": "https://apiBaseUrl/presigned-url-to-evidence.webp",
      "evidenceIds": ["6a4c86ac0e6682736f8a55cb"]
    }
  ],
  "recordings": []
}

Property Breakdown:

Top-Level Properties:

  • sessionId (string): Unique identifier for the proctoring session.
  • summary (object): Overview metrics of the entire session.
  • violations (array): List of captured infractions or anomalies.
  • recordings (array): References to full video/screen recordings if continuous recording was enabled.

Summary Properties:

  • candidateId (string): The ID of the candidate provided during session provisioning.
  • sessionStatus (string): The terminal state of the session (e.g., COMPLETED, TERMINATED, ABANDONED).
  • startedAt (string, ISO-8601): When the session physically began streaming telemetry.
  • endedAt (string, ISO-8601): When the session physically stopped.
  • durationSeconds (number): Total active proctoring time.
  • integrityScore (number): Calculated trustworthiness score (0-100) based on severity of violations.
  • totalViolations (number): Total count of violations triggered during the session.

Violation Properties:

  • id (string): Unique identifier of the specific violation occurrence.
  • violationType (string): The underlying raw classification metric (e.g., THRESHOLD_BREACH, FACE_MISSING).
  • eventType (string): Human-readable name of the rule that was breached (e.g., "Window Minimized", "Multiple Faces Detected").
  • severity (string): The severity level assigned to the rule (LOW, MEDIUM, HIGH, CRITICAL).
  • capturedAt / timestamp (string, ISO-8601): Time when the violation was first detected.
  • evidenceUrl (string): Expiring pre-signed URL to view the snapshot/video evidence of the violation.
  • evidenceIds (array): Internal reference IDs mapping to the underlying evidence records.