Basic Concepts

Last updated
July 24, 2025

Introduction

This section provides a structured reference for integrating the Saphere Scan widget into your application. Whether you're working on a web platform or a mobile environment, Saphere Scan offers a consistent API surface and lightweight deployment.

Integration Overview

  • Web: Embed the widget into any HTML page using the CDN script.
  • React Native: Use a local HTML file rendered inside a WebView.
  • Ionic: Integrate similarly to React Native via WebView.

Communication Flow Summary

Below is a high-level sequence diagram that illustrates the communication between the Saphere Scan widget on the frontend and the Saphere Core backend:

Communication Flow Summary

Integration Handler

The Handler object is the primary interface for mounting, unmounting, and configuring the Saphere Scan widget.

interface Handler {
  load: (container: string | HTMLDivElement, options: Options) => void;
  destroy: () => Promise<void>;
  langs: Lang[];
}

Properties

load(container, options)

  • Mounts the widget in the provided container.
  • container: Either a CSS selector string or a HTMLDivElement.
  • options: A configuration object (see Options reference).
  • Example:
Handler.load("#container", options);

destroy()

  • Asynchronously unmounts and removes the widget from the DOM.
  • Returns a Promise<void> that resolves once the widget is cleaned up.
  • Example:
await Handler.destroy();

langs

  • Returns an array of available language codes supported by the widget.
  • Example output:
["ar", "de", "en", "es", "fr", "it", "pl", "pt", "pt_BR", "tr"]

Integration Options

Full Options Reference

The full configuration object passed to Handler.load() looks like this:

interface Options {
  createMeasure: CreateMeasureOptions;
  onEvent?: (event: Event) => Promise<boolean | undefined>;
  allowLeave?: boolean;
  lang?: string;
}

  • createMeasure (required): Defines how the measure is created (delegate or handle strategy).
  • onEvent: Callback that listens to events during the scan (start, result, aborted, etc.).
  • allowLeave: Show/hide the "Leave" button in the widget.
  • lang: Set the default language (en, fr, es, etc.).

Create a measure

You can create a measure using one of two strategies:

1. Delegate Strategy (recommended)
Use this strategy to delegate the retrieval of the measurement to the widget. You must provide an authenticated endpoint (e.g., via an API key or token). The widget will make a POST request to that URL to create a new measurement.

  • Delegate: Most common approach; widget will POST to a secure URL on your backend to create a measure.
createMeasure: {
  strategy: "delegate",
  url: "https://api.test.saphere.ai/measures",
  headers: { Authorization: `Bearer <YOUR_API_KEY>` }
}

This approach is fast and secure but requires proper access control on your backend.

2. Handle Strategy (advanced/custom logic)
Instead of calling a remote URL, you provide a function (fetch) that is responsible for returning a new measurement ID. This is useful if you want to run custom checks or workflows before the scan starts.

  • Handle: Advanced control; your frontend provides a fetch() function returning a measure object.
createMeasure: {
  strategy: "handle",
  fetch: async (options: FetchOptions) => {
    // custom logic here
    return { id: "...", ... }
  }
}

Sell also FetchOptions

Widget Events

The widget emits structured events you can use to monitor or react to user behavior:

start: User has clicked to begin the process

record: Recording has started

end: Measurement session has ended

result: Results are available

aborted: Measurement was interrupted

leave: User clicked "Leave"

Each event provides contextual information — for example, the result event includes the computed variables, and aborted includes the reason.

Aborted Reasons Reference

Here are possible causes for an aborted scan:

  • CANCELED_WHILE_COUNTDOWN: The user clicked on the cancel button during the countdown step.
  • CANCELED_WHILE_CAPTURING: The user clicked on the cancel button during the capturing step.
  • CANCELED_WHILE_SENDING: The user clicked on the cancel button during the sending step.
  • CANCELED_WHILE_PROCESSING: The user clicked on the cancel button during the processing step.
  • WIDGET_RESIZED: The widget has been resized during the capture process.
  • FOCUS_LOST: The browser window lost the focus.
  • VIDEO_SOURCE_ENDED: The video stream is no longer accessible.
  • COMPONENT_UNMOUNTED: The widget has been unmounted.
  • UNEXPECTED_WS_CLOSED: Connection unexpectedly closed.
  • CONNECTION_ERROR: Cannot connect to the server.
  • CONFORMITY_MISSING_SIGNAL: Missing face.
  • CONFORMITY_MUCH_VARIATIONS: Too muchs variations.
  • CONFORMITY_POOR_LIGHT: Insufficient light.
  • CONFORMITY_LOW_FPS: The device is too slow to process the capture.
  • CONFORMITY_FACE_SIZE: Face too small.
  • CREDIT_EXCEEDED: The credit limit has been reached.
  • MISSING_USER_DATA: The user data form is not enabled in customization or missing in the api request.
  • MISSING_USER_DATA_WEIGHT: The user data form is not enabled in customization or the weight missing in the api request.
  • MISSING_USER_DATA_HEIGHT: The user data form is not enabled in customization or the height is missing in the api request.
  • MISSING_USER_DATA_SEX: The user data form is not enabled in customization or the sex is missing in the api request.
  • MISSING_USER_DATA_AGE: The user data form is not enabled in customization or the age is missing in the api request.
  • MISSING_USER_DATA_SMOKING_STATUS The user data form is not enabled in customization or the smoking status is missing in the api request.

Languages

The widget supports multiple languages. Use the lang option to enforce a default:

lang: "en" // or "fr", "de", "pt", etc.

Available values: ar, de, en, es, fr, it, pl, pt, pt_BR, tr

User Data Input Options

Some physiological variables (e.g., BMI from face Scan, cardiovascular health score, high blood pressure risk) require additional user context like age, sex, weight, height, or smoking status. The widget supports two integration modes to handle this information:

1. Programmatic Injection (userData via FetchOptions)

If you already collect user data in your application (e.g., from a sign-up form or user profile), you can inject it directly when creating a measure.

Example:

createMeasure: {
  strategy: "handle",
  fetch: async (options) => {
    console.log(options.userData) // auto-populated if using form, or inject it yourself

    return await createMeasureInBackend({
      userData: {
        age: 30,
        sex: "M",
        weight: 72,
        height: 178,
        smokingStatus: "non-smoker"
      }
    });
  }
}

This approach gives you full control over the user experience and data validation. You must ensure all required fields are present to prevent aborted scans due to MISSING_USER_DATA_* errors.

2. UI-Based Collection (via User Data Form in Customization)

If you'd prefer to let the widget collect user information, Saphere Scan provides a built-in form — enabled via your Customization ID.

How to enable it:

  1. Go to the Customization Creator in the Saphere Ops portal
  2. Enable the User Data Form
  3. Copy the customizationId
  4. Apply the customization by appending the customizationId as query parameter of the widget script url  and load the widget:

Once enabled:

  • The form will be shown before measurement begins
  • The widget will validate the inputs
  • The form-submitted userData will:
    • Be passed to your fetch() function (if using handle strategy)
    • Be included in the POST body (if using delegate strategy)

🔐 Note: You're still responsible for using the collected data to create the measure on the backend.

👉 See also Metrics Dependencies Overview.

👉 See also how to Create and Apply Customization.

FetchOptions Reference

When using the "handle" strategy in createMeasure, your custom fetch() function receives a FetchOptions object. This object lets the widget pass relevant user context and requested variables.

interface FetchOptions {
  userData?: UserData;
  desiredVariables?: Variable[];
  unwantedVariables?: Variable[];
}


Fields

userData (optional)

Contains user-specific information such as:

{
  age?: number;
  sex?: "M" | "F";
  weight?: number;
  height?: number;
  smokingStatus?: "smoker" | "non-smoker";
}


📌Note: These values depend on how you configure the user data form (or feed data manually).

👉 See also Metrics Dependencies Overview.

desiredVariables (optional)

An array of variables that the client wants to compute (e.g., ["hr", "br", "faceBmi"]). Use this to restrict or validate what’s computed server-side.

By default, all variables included in your subscription are automatically calculated.

You can refine this selection using the desiredVariables (variables you want) and unwantedVariables (variables to exclude) parameters.

unwantedVariables (optional)

Same as above, but for excluding variables explicitly. If you receive both desiredVariables and unwantedVariables, desiredVariables takes precedence.

Example usage

fetch: async (options: FetchOptions) => {
  console.log("User wants:", options.desiredVariables);
  console.log("User data:", options.userData);
  const res = await fetch("/api/create-measure", {
    method: "POST",
    body: JSON.stringify(options),
    headers: { "Content-Type": "application/json" }
  });
  return await res.json(); // Must return a valid CreatedMeasureDTO
}

📌Note: If you fill both desiredVariables and unwantedVariables, desiredVariables takes precedence.

Create Your Proxy

To use the widget in delegate mode, your application must expose a server-side proxy that securely handles the creation of measurement sessions.

This proxy acts as an intermediary between the Saphere Scan frontend and the Saphere Core backend, enabling secure authentication and control over who can start a scan.

Proxy integration

Why a Proxy?

  • The frontend (e.g. browser) should never expose API keys directly.
  • The widget needs to retrieve a measurement token before it can open a WebSocket.
  • Your backend will handle authentication and access control before requesting the token from Saphere Core.

Expected Proxy Behavior

Your backend must implement an HTTP endpoint that accepts the measurement request. It should forward the request to the official Saphere Core API, adding the required headers:

POST /api/my-measure-proxy
Authorization: Bearer <your-backend-token>
Content-Type: application/json

{
  "userData": {
    "age": 30,
    "height": 175,
    "weight": 68,
    "sex": "M"
  },
  "desiredVariables": ["hr", "hrv", "bpClass"]
}


This endpoint will forward the request to:

POST https://api.test.saphere.ai/measures

And return the CreatedMeasureDTO object to the widget.

Proxy Benefits

  • Protects your credentials
  • Allows custom validation and logging
  • Enables quota and user tracking
  • Keeps your frontend lightweight and secure

📌 Note: If you're using the handle strategy instead of delegate, your proxy is implemented directly inside your frontend logic using a fetch() function.

Close Modal