Options
Tout ce que Handler.load() accepte : la stratégie de mesure, la fonction de rappel d’événements, la palette de onze couleurs, les écrans et les textes.
Une nouvelle intégration devrait utiliser Saphere Scan v2. Ce qui suit documente l’objet d’options de la v1.
Vos options sont validées, puis fusionnées par-dessus les valeurs par défaut — et par-dessus toute personnalisation appliquée par ?customizationId=. Les clés de premier niveau inconnues sont refusées plutôt qu’ignorées.
Premier niveau
| Clé | Type | Défaut | Remarques |
|---|---|---|---|
createMeasure | objet | — | Requis. Comment une mesure est obtenue. |
onEvent | fonction | — | Appelée à chaque événement. |
allowLeave | booléen | false | Affiche une commande permettant à l’utilisateur de quitter le widget. |
allowSkip | booléen | false | Permet à l’utilisateur de passer les écrans d’accueil. |
lang | chaîne | langue du navigateur, sinon "en" | L’une des dix langues prises en charge. |
useShadow | booléen | true | Monte dans un shadow root. |
desiredVariables | string[] | — | Réduit ce qui est calculé. |
tags | string[] | — | Conservés avec la mesure. |
Les écrans et les couleurs ne sont pas dans cette liste. Ils se préparent dans la console, non dans votre code — voir l’apparence plus bas.
`useShadow: false` laisse votre CSS entrer
Par défaut, le widget est monté dans un shadow root, ce qui empêche votre feuille de style de l’affecter et la sienne de vous affecter. Désactiver cela est parfois nécessaire dans les coquilles hybrides ; attendez-vous alors à ce que votre CSS global atteigne les entrailles du widget.createMeasure
Le widget a besoin d’une mesure à laquelle rattacher la capture. Il existe deux stratégies, et elles diffèrent par qui fait l’appel.
delegate — le widget appelle votre point d’accès
createMeasure: {
strategy: "delegate",
url: "/api/saphere-measure",
headers: { "X-Session": sessionId } // facultatif
}
Le widget émet POST <url> avec un corps JSON portant userData, desiredVariables et tags, et attend { "id": "<measureId>" } en retour.
Votre point d’accès authentifie votre utilisateur, appelle POST /measures sur l’API Saphere avec votre clé, et relaie l’identifiant.
handle — votre code fait l’appel
createMeasure: {
strategy: "handle",
fetch: async ({ userData, desiredVariables }) => {
const response = await fetch("/api/saphere-measure", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userData, desiredVariables })
})
return await response.json() // doit être { id: "…" }
}
}
`fetch` doit résoudre vers un objet portant un `id`
Rendre l’identifiant sous la forme d’une chaîne nue lève Cannot get measureId!. Le contrat est { id: string } — un objet.
Cela vaut d’être vérifié en premier quand une intégration v1 échoue dès le départ : certains exemples publiés rendaient json["token"], qui est une chaîne, et ne le satisfait pas.
Si le point d’accès répond avec un statut hors 2xx, le widget lève Cannot create measure!.
onEvent
onEvent: async event => {
console.log(event.type, event)
}
Appelée pour chaque événement ci-dessous. La valeur rendue est significative : rendre littéralement false annule la transition que l’événement annonce. Tout le reste l’autorise, y compris undefined, et y compris une exception levée par votre propre code, qui est journalisée et ignorée.
Le gestionnaire est attendu.
Un gestionnaire lent ralentit le widget
Parce que la valeur rendue peut opposer un veto à une transition, la v1 attend votre gestionnaire avant de poursuivre. Un gestionnaire qui fait un appel réseau à chaque événement retarde le parcours utilisateur d’autant.
C’est le comportement que la v2 a délibérément abandonné : là, onEvent n’est jamais attendu, et le veto a migré vers une API hooks distincte, bornée par un délai.
L’union des événements
type | Charge utile | Quand |
|---|---|---|
video-stream-loading | { status: "start" } | La caméra est demandée |
video-stream-loading | { status: "ready" } | Le flux est ouvert |
video-stream-loading | { status: "error", reason } | La caméra a été refusée |
start | — | Le visage est accepté et le cadrage figé |
record | — | La capture commence |
end | — | Toutes les images sont mises en file et le marqueur de fin est en attente |
result | { id, userData?, variables } | La mesure a abouti |
aborted | { reasons } | La mesure s’est arrêtée |
leave | — | L’utilisateur a demandé à quitter (exige allowLeave) |
transition | { from, to } | Déclaré, mais jamais émis |
Le reason de video-stream-loading vaut "denied", "no-device", "not-supported" ou "already-used".
Le reasons d’aborted porte soit un code de conformité (CONFORMITY_POOR_LIGHT, CONFORMITY_MUCH_VARIATIONS, CONFORMITY_FACE_PRESENCE, CONFORMITY_LOW_FPS, CREDIT_EXCEEDED, MISSING_USER_DATA_*, …), soit un motif d’échec (WIDGET_RESIZED, FOCUS_LOST, CANCELED_WHILE_CAPTURING, CREATE_MEASURE_ERROR, WS_CONNECTION_ERROR, VIDEO_SOURCE_ENDED, …).
`transition` n'est jamais émis
TransitionEvent fait partie du type déclaré, et un gestionnaire qui l’aiguille compilera. Rien dans le module n’en construit. N’y bâtissez pas un suivi de navigation.Dans un événement result, chaque variable vaut { value, signals, error: null } en cas de succès, ou { value: null, error } en cas d’échec.
L’apparence : écrans et couleurs
L’apparence du widget n’est pas décrite dans les options que vous passez dans votre code. Elle se prépare dans la console ops, avec l’atelier de personnalisation, et s’attache au widget par son identifiant.
Chaque personnalisation est listée sous Scan tools → Scan customization, avec l’identifiant que vous utiliserez.

En ouvrir une donne l’atelier. Un widget en fonctionnement se tient à côté des réglages, de sorte que ce que vous approuvez est ce que vos utilisateurs verront.
Colors — la palette que le widget s’applique.

Pages — les écrans que le parcours comporte, ceux qu’il laisse de côté, et le logo affiché en tête.

Une fois la personnalisation enregistrée, ajoutez son identifiant à l’adresse depuis laquelle vous chargez le module :
<script src="https://cdn.saphere.ai/saphere-scan/v1/js/main.min.js?customizationId=8f1c2d34-5a6b-4c7d-8e9f-0a1b2c3d4e5f"></script>
Le module arrive en la portant déjà. Rien ne change dans votre code, et modifier la personnalisation plus tard prend effet au chargement suivant, sans aucun déploiement de votre côté.
Préparez-la dans l'environnement où vous chargez
Une personnalisation enregistrée sur le Test n’est pas le même enregistrement qu’une personnalisation enregistrée en Production : son identifiant ne s’y transpose pas. Préparez-la là où vous l’utiliserez.