Confirm the host has upgraded to a runtime-content-enabled Vue package and that its configured apiBaseUrl or standalone ingestUrl points to the Proctor API. Published content is loaded automatically; there is no content opt-in prop. In browser network tools, check the schema manifest GET: a changed production release returns 200, while unchanged content may return 304 from ETag revalidation. A timeout, CORS/CSP block, incompatible response, or invalid manifest falls back to bundled copy without blocking the assessment. Host messages overrides have highest priority and can intentionally hide a published platform change.
Confirm the request uses the schema line supported by the installed package. The current package requests /schemas/4/; older packages intentionally continue using their older schema production pointer. Saving a draft is not enough: Content Studio must show the change in a published production release before a candidate refresh can receive it.
Locale changes during a mounted assessment use the release pinned at initial load. Refresh the page to move to a newly promoted release. If an emergency rollback was published, a refresh should revalidate the production pointer; no npm package update is required.
The assessment is intentionally withheld until the Proctor API accepts a
disposable event through the normal ingest path. Sessions with continuous
screen or webcam recording must also persist and remove a 1 KiB canary through
the server-selected relay or direct-storage path. This check is mandatory and
cannot be disabled by policy or SDK configuration.
Check the browser network panel for the ingest request, /public/upload-config,
and—when recording is enabled—/uploads/readiness/.... Common causes are an
invalid app ID, an origin missing from the app-key allow-list, CORS rejection,
DNS or proxy filtering, privacy extensions, and direct-storage CORS rules. Host
code can use DeliveryReadinessError.code for diagnostics; candidate UI should
show only the neutral retry message.
Browsers require a user gesture to call getDisplayMedia. In raw-SDK integrations, call requestScreenShare() as the first action in the candidate's button handler and pass its stream through observers.screenShare.stream. The compatibility screenShare: true shortcut also opens the picker synchronously during construction, so the client must still be constructed directly from that button handler. The Vue wrapper handles both initial sharing and recovery from its candidate actions. Effects, timers, worker messages, and unrelated asynchronous work do not provide the required activation.
Safari 16.4+ is the minimum supported desktop version, but camera/screen prompts remain sensitive to direct user interaction. If a custom flow awaits unrelated work before constructing the raw client, Safari can refuse the prompt. Construct ProctoringClient directly from the candidate click or acquire the stream in that click. Also note that the current post-session Python face analyser skips Safari MP4 recordings; preflight JPEG face detection is unaffected. See the browser matrix.
They don't. Every event has a monotonic timestamp at the SDK, assigned before queueing. The dashboard sorts by timestamp, not by ingest order, so out-of-order uploads (common over flaky networks) appear in correct chronological order.
No, and the design protects integrity events. Media chunks and integrity events ride separate queues with separate byte budgets, so media backing up under a flaky network can never push integrity events out. When the event queue exceeds its byte budget (50 MiB by default), it drops the oldest entries first — the event being enqueued is never the one dropped — and the worker reports an overflow message with the dropped count, so a full queue is observable rather than silent.
Browsers do not guarantee long async work during page close. The SDK flushes what it can, and the backend infers abandonment from missing heartbeat/activity when a clean session.ended does not arrive.
By default, microphone speech detection assets are served by the Proctor API under /sdk-assets/vad/ and ONNX runtime assets under /sdk-assets/ort/. Set explicit asset URLs only if you want to serve those files from your own CDN.
The wizard starts preparing these assets when the microphone step mounts. If the model/runtime is still unavailable 10 seconds after Test microphone, it automatically uses its internal RMS fallback; the client cannot select or disable that path. Check asset requests, compression/cache headers, proxy filtering, and MIME types. A failed speculative prefetch does not poison the later VAD loader, and a late VAD instance is destroyed rather than taking control back from the fallback.
Recording chunks upload directly from the candidate's browser to storage, so the exam page's origin must be allowed by the storage bucket's CORS rules — not just by the API. The server keeps bucket CORS in sync from each app key's allowed origins, so the fix is to add every candidate-facing origin to the app key's allowed origins (Settings -> Keys). An origin that is missing shows up as unrecoverable chunk-upload failures in the browser network panel, and post-session analysis then reports failed because there is no recording to analyse. Self-hosted deployments can additionally pin first-party origins with the S3_CORS_ORIGINS environment variable.
Deliberate. Device-picker selections survive a refresh, but verification verdicts never do — a stale "we heard speech ten minutes ago" pass would let a candidate cover the mic during the test, so every refresh re-runs the checks. Don't build a flow that relies on preflight state surviving a reload; treat a refresh as a fresh run of the wizard.
The server gates runtime ingest on the preflight pass. With the default strict gate, session.* events for a session whose latest wizard attempt has not passed are rejected with 403; batches made entirely of preflight.* events are never gated. Make sure the wizard completes and passes before you construct the runtime client with the same id. If you intentionally skip preflight (staging, SDK-only debugging), run the deployment with PREFLIGHT_GATE=warn or PREFLIGHT_GATE=off.
Three things trip automated runs. First, use Chromium's fake capture devices: launch with --use-fake-device-for-media-stream and grant camera + microphone permissions through your test runner's context permissions (in Playwright, the permissions option) rather than --use-fake-ui-for-media-stream, so getUserMedia resolves through the real granted-permission path. Second, the mic step's speech check runs on Web Audio, which the legacy headless shell does not implement — in Playwright, run with channel: "chromium" so the new headless mode (full Chromium) is used. Third, the speech check classifies speech vs. steady noise, so the fake mic's default beep never passes: pipe a real speech recording in with --use-file-for-fake-audio-capture=<speech>.wav (16-bit PCM WAV).