Raw SDK integration
Build a custom UI with SystemCheck and ProctoringClient, including readiness, activation, recovery, and clean teardown.
On this page
Use @a4anthony/proctorkit-sdk when you own the candidate UI or are building a framework wrapper. The SDK is shipped and framework-agnostic. It exposes the preflight engine, runtime client, observer helpers, media controls, and typed failure surfaces; it does not provide assessment screens.
The raw SDK is a lower-level contract. Your host must resolve attempts, render preflight, translate a policy into engine/runtime options, preserve user activation for browser prompts, lock the assessment after required evidence stops, and end the session cleanly. Use the Vue wrapper unless you need that control.
Installation
pnpm add @a4anthony/proctorkit-sdkThe worker is a separate browser asset. Let your bundler resolve the package export:
const workerUrl = new URL(
"@a4anthony/proctorkit-sdk/worker",
import.meta.url,
);End-to-end shape
The customer-generated correlationId identifies the assessment attempt across preflight and runtime. The server creates an internal sessionId (sess_…) for each sitting. Resolve that sitting before running the raw flow; do not invent or reuse an internal session id.
import {
ProctoringClient,
SystemCheck,
type WorkerToMainMessage,
} from "@a4anthony/proctorkit-sdk";
const correlationId = "lms-attempt-8042";
resolution
method
headers
body correlationId
response response
sessionId resolutionsessionId
check
mode
media camera microphone speaker
system browser device layout connection
unsubscribe checkrows rows
report check
reportpassed
ready
endController
client
appId
sessionId
ingestUrl
workerUrl metaurl
candidate id
policySnapshot resolvedPolicy
observers compiledObservers
endSignal endControllersignal
message
message ready
message
error
error
kind
kind
kind
kind
clientThe exact attempt-resolution response is documented in the Server API. Preflight telemetry and identity-photo upload require additional wiring; study the preflight contract before replacing the Vue wizard.
Constructor options
| Option | Required/default | Purpose |
|---|---|---|
sessionId | required | Internal sitting id returned by attempt resolution. |
ingestUrl | required | Full public ingest endpoint. |
appId | recommended | Browser-safe pk_ key for tenant/application scope. |
workerUrl | production required | Bundled Worker entry. workerFactory is the mutually exclusive test seam. |
candidate | optional | Stable candidate id plus optional name/email/metadata. |
policySnapshot |
The client also exposes host actions for custom events, checkpoints, media playback/recording, screen-share restart/deferred recording, webcam restart, device settings, and text-answer recording. Use the focused Observers, Media helpers, and Writing answers references for those method contracts.
Lifecycle contract
new ProctoringClient(options) starts asynchronously in the constructor. There is no public start() method.
- If screen sharing is configured without a supplied stream, the constructor opens the browser picker immediately to retain transient user activation.
- The worker opens its queue and must deliver an ingest canary.
- Recording sessions must also complete a disposable storage canary through the server-selected upload path.
- Media and DOM observers start.
- The worker sends
{ type: "ready" }; the SDK then emitssdk.ready.
client.emit() throws before worker readiness. The readiness signal proves startup delivery, not that every later network request will succeed; keep observing uploaded, upload-failed, and dropped messages. See Evidence capture and delivery.
Clean end and abandonment
Always await client.end() after the host has saved the candidate's assessment response. It is idempotent and:
- emits
session.end_requested; - stops observers and flushes recorder tail chunks;
- drains queued media;
- emits and flushes
session.ended; - exits fullscreen, releases owned tracks, and terminates the worker.
Aborting the supplied endSignal calls the same clean-end path. pagehide only asks the worker to flush events because browsers cannot reliably distinguish refresh from close or await a full media drain. A closed tab with no terminal event becomes abandoned after server-side activity staleness.
Screen-share activation
Construct the client directly inside the candidate's click handler when raw SDK screen sharing will request its own stream:
startButton.addEventListener("click", () => {
const client = new ProctoringClient({
// ...identity, endpoint, worker and error callbacks
observers: { screenShare: { enforceEntireScreen: true } },
});
mountAssessment(client);
});Do not await a fetch, telemetry flush, or modal promise between the click and construction. Alternatively, call requestScreenShare() from the click and pass the returned stream to the observer. Re-starting after the candidate stops sharing must also happen from a fresh click. The Vue wrapper provides this prompt/recovery flow.
Unsupported raw configurations
The raw observer layer can technically enable webcam snapshots and continuous recording together. The assessment policy deliberately exposes one webcam.mode — none, snapshots, or recording — and never enables both. Custom wrappers should preserve that invariant to avoid duplicate evidence, higher bandwidth/storage, and confusing review semantics.
Do not expose delivery canaries, VAD fallback selection, worker factories, or internal request deadlines as candidate/customer policy controls. workerFactory is a test seam; production integrations should provide workerUrl.
Failure handling checklist
Implement all of these before a trial:
- Block assessment entry until worker readiness and required media have started.
- Render actionable UI for
onError,onScreenShareError, andonWebcamError. - Monitor worker upload-health messages and decide when degraded delivery must pause the test.
- Lock the test when required screen sharing or webcam capture ends; offer a user-activated recovery action.
- Save the assessment answer in your own backend before
await client.end(). - Reconcile session outcome from your backend; do not rely on a browser callback or webhook alone.
- Test refresh, offline/online, denied permissions, device removal, storage rejection, and end-drain timeout paths.
Next: policy configuration, browser and network support, and controlled-trial checklist.