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:
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
orhandle
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 willPOST
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 afetch()
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:
- Go to the Customization Creator in the Saphere Ops portal
- Enable the User Data Form
- Copy the
customizationId
- 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 usinghandle
strategy) - Be included in the POST body (if using
delegate
strategy)
- Be passed to your
🔐 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.
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.