VerifyCameraV3 · 3.3.1

A fast, guided camera

A standalone browser SDK for a clear face photo or a short randomized live selfie. It coexists with the legacy camera and never replaces window.VerifyCamera.

Install

<script src="https://www.verifyfaceid.com/camera-v3.js?v=3.14.0"></script>

The SDK begins loading its pinned, self-hosted face detector in the background. Call preload() earlier if the script itself is loaded on demand.

await VerifyCameraV3.preload();

Capture a live selfie

try {
  const selfie = await VerifyCameraV3.capture();

  // Upload-ready JPEG Blob
  formData.append('selfie', selfie.blob, 'selfie.jpg');

  // Optional local preview
  preview.src = selfie.image;
} catch (error) {
  if (error.code !== 'cancelled') {
    console.error(error.code, error.message);
  }
}

The default liveness mode guides the person through a short on-screen challenge and then takes the photo automatically once the result is a settled, front-facing frame. The challenge is generated fresh for every attempt, so it cannot be rehearsed or prepared in advance. Clear guidance is shown throughout for framing, lighting, and each requested movement. A failed attempt can be retried up to five times without reopening the camera or repeating the permission prompt.

Designed for a quick phone flow

The full-screen controls respect phone safe areas, work in portrait and landscape, and keep a labeled Cancel action visible throughout. Retries are automatic: each gets a new challenge while the existing camera stream stays open, so a stumble never requires a tap.

Complete v3 flow

// A trusted reference needs a clear centered photo, not a live challenge.
const reference = await VerifyCameraV3.capture({ mode: 'photo' });

// The fresh selfie receives the randomized live challenge.
const selfie = await VerifyCameraV3.capture({ mode: 'liveness' });

const body = new FormData();
body.append('reference', reference.blob, 'reference.jpg');
body.append('selfie', selfie.blob, 'selfie.jpg');

// Send the Blobs to your own backend. Your backend forwards the multipart
// request to VerifyFaceID and adds its Bearer API key there.
const response = await fetch('/your-backend/verify-face', {
  method: 'POST',
  body
});
const result = await response.json();

Keep the API key on your server

VerifyFaceID API keys are long-lived credentials. Do not embed one in JavaScript shipped to a public browser. Let your backend add the Authorization: Bearer header when it forwards the two files to v3.

Capture result

{
  image: "data:image/jpeg;base64,…",
  blob: Blob,
  mimeType: "image/jpeg",
  width: 900,
  height: 900,
  size: 183204,
  liveness: {
    performed: true,
    passed: true,
    challenge: "turn_left",
    durationMs: 1842
  }
}

In photo mode, performed is false and the other liveness values are null. The image is cropped around the detected face, encoded as JPEG, and kept below the API’s upload limit.

For compatibility with earlier v3 integrations, liveness.challenge remains "turn_left" or "turn_right" and identifies the first on-screen prompt. The remainder of the challenge is intentionally not exposed in the result object.

Public methods

MethodReturnsPurpose
capture(options?)Promise<Capture>Open the guided camera and return the structured result.
takePicture(options?)Promise<string>Convenience method returning only the JPEG data URL.
preload()Promise<true>Warm the SDK runtime and its detection assets before capture.
cancel()voidCancel the active capture and stop all media tracks.
isSupported()booleanQuick feature check.
getSupport()objectReturns supported and a machine-readable reason.

Options

OptionDefaultNotes
mode"liveness"liveness or the centered photo mode.
facingMode"user"user or environment; requested as an ideal constraint.
mirrortrue for front cameraMirrors the preview and direction calculation. The saved JPEG is not mirrored.
signalnullAn AbortSignal for application-driven cancellation.
challengeTimeoutMs20000Time allowed to complete the on-screen challenge, clamped from 6 to 30 seconds. Shorter values automatically receive a shorter challenge.
maxAttempts6One initial attempt plus five retries by default; total attempts are clamped from 1 to 10. Retries use a new challenge and the same camera stream.
permissionTimeoutMs30000How long an unanswered browser permission may remain open.
maxDimension900Square JPEG dimension, clamped from 480 to 1280.
jpegQuality0.90Clamped from 0.80 to 0.95 and reduced if needed for size.

Stable error handling

All SDK failures reject with VerifyCameraV3.Error. Check error.code, not browser-specific error text.

try {
  await VerifyCameraV3.capture();
} catch (error) {
  switch (error.code) {
    case 'cancelled':
    case 'aborted':
      break;
    case 'permission_denied':
      showCameraPermissionHelp();
      break;
    default:
      showRetryMessage(error.message);
  }
}

Common codes include camera_busy, insecure_context, camera_blocked, camera_unsupported, permission_denied, permission_timeout, camera_not_found, camera_in_use, model_load_failed, face_timeout, challenge_failed, challenge_timeout, camera_interrupted, and capture_failed. The boolean recoverable property helps separate unsupported environments from retryable failures.

Browser and permission requirements

  • Serve the page over HTTPS. Browsers expose getUserMedia() only in a secure context.
  • Request camera access in response to a user action, such as a button click.
  • No microphone permission is requested.
  • For an iframe, the top-level site must allow camera use and the iframe must include allow="camera".
  • Use getSupport() before presenting the capture button when supporting older or embedded browsers.
<iframe
  src="https://your-app.example/verify"
  allow="camera"
></iframe>

Content Security Policy

If your site uses a strict CSP, allow the hosted SDK and its runtime in script-src and allow its asset downloads from the same origin in connect-src. When the main SDK script has a nonce, v3 copies it to the script and style it injects; otherwise the injected style must be allowed by style-src.

What the live challenge does—and does not prove

The challenge begins only after the SDK has a clear, well-lit view of a single face, and it is generated fresh for each attempt using a cryptographic random source. Throughout the flow the SDK verifies that it is watching one continuous face and that each response is newly performed rather than a held pose or a stale reading, and the automatic capture waits for a settled, front-facing frame suitable for face comparison. The signals, thresholds, and sequencing behind the challenge are internal to the SDK and may change between versions without notice; do not build integrations that depend on them.

A randomized motion check—not certified liveness

The challenge raises the effort needed to pass with a static photo or a screen held in front of an ordinary camera, and it produces a fresh, well-framed selfie. Because it runs entirely in the visitor’s browser, it cannot prove that a live person was physically present, and a modified client can bypass it. Treat the liveness object as a usability and deterrence signal, never as a signed attestation, and use a server-verified liveness service when your threat model requires stronger assurance.

Coexisting with the legacy camera

<script src="https://www.verifyfaceid.com/camera.js?v=2.0.27"></script>
<script src="https://www.verifyfaceid.com/camera-v3.js?v=3.14.0"></script>

// Existing integration remains available:
await VerifyCamera.takePicture();

// New integration uses a different global:
await VerifyCameraV3.capture();

The globals can coexist during a gradual migration. If both scripts are needed on one page, load the legacy script first as shown and do not open both camera generations at the same time. New pages should load only v3 for the smallest, fastest path.