Upgrading from v1
What changes between the v1 module and Saphere Scan v2, in the order you will meet it.
Version 2 is a separate module rather than an update of version 1. An integration does not migrate itself: you replace the script, rename a handful of options, and adapt the way you obtain a credential.
Nothing on the server changes. The same account, the same measurements, the same variables. What changes is the code in your page.
The five changes, in order
| v1 | v2 | |
|---|---|---|
| The script | A classic script, or an ES module, from /saphere-scan/v1/… | An ES module, from /saphere-scan/v2/main.js |
| Mounting | Handler.load("#container", options) | SaphereScan.create("#container", options) then bootstrap() |
| The credential | Your endpoint creates a measure and returns its id | Your endpoint issues a short-lived access token |
| Events | Eight names, and your handler, awaited, could hold the widget back | Forty-four names, never awaited, and refusing moved to hooks |
| Appearance | Prepared in the console | Prepared in the same console, as a v2 customisation |
1. The script and the mounting
<!-- v1 -->
<script src="https://cdn.saphere.ai/saphere-scan/v1/js/main.min.js"></script>
<script>
Handler.load("#scan", options)
</script>
<!-- v2 -->
<script type="module">
import SaphereScan from "https://cdn.saphere.ai/saphere-scan/v2/main.js"
const instance = SaphereScan.create("#scan", options)
await instance.bootstrap()
</script>
Two differences behind that. create() returns an instance rather than acting on a global, so several widgets can coexist on one page. And mounting is now in two steps: create() validates your options straight away and throws if something is wrong, bootstrap() puts the widget on screen.
The container contract is the same: you give a div with a height, the widget fills it, and it keeps its styles to itself.
2. The credential
This is the change that needs your server, and the reason v2 exists.
In v1, your endpoint created a measure and returned its identifier. That identifier opened the measurement session.
In v2, your endpoint issues an access token: valid for the duration you choose — 48 hours at most —, opening no more measurement sessions than you allow — one, typically —, and carrying only the permission to measure. Your API key stays on your server in both versions, but a token that leaks is worth almost nothing, where a measure identifier was worth a whole measurement.
// v1
createMeasure: { strategy: "delegate", url: "/api/saphere-measure" }
// v2
proxy: { retrieveAccessToken: { strategy: "delegate", url: "/api/saphere-token" } }
What your endpoint no longer carries is the measurement itself. In v1 you described it in the body of the call that created it; in v2 it is an option of the widget, createMeasureOptions, where the patient data you already hold goes. The token authorises; the option describes.
The strategies keep their names and their meaning: delegate for the widget to call your endpoint, handle for your own code to obtain the value. Access tokens covers what your endpoint has to call.
3. The events
v1 declared eight events — one of them, transition, never actually emitted; v2 reports forty-four, and none of the old names survives. The naming is now domain:event, so result became measure:result, start became placement:accepted, and so on. The full list, with a v1-to-v2 table, is in Events.
One behaviour moved rather than being renamed. In v1, your onEvent handler was awaited, and returning false stopped the widget from moving on to its next step — which meant a slow handler could hold the interface. In v2, onEvent is never awaited, and the power to refuse moved to hooks, where it is bounded by a guard delay.
4. The options that changed name
| v1 | v2 |
|---|---|
createMeasure | proxy.retrieveAccessToken |
onEvent | Same name, never awaited, return ignored — see above |
allowLeave | Gone: v2 offers no command to leave the widget; your page decides when to destroy() it |
allowSkip | Gone as a link for the user; which guidance screens the journey includes is part of the customisation |
colors, texts, pages | Part of the customisation, in the console; in code, under components |
isMobile | Gone |
useShadow | Gone; the widget always keeps its styles to itself |
lang | Unchanged |
desiredVariables, tags | Unchanged, and now joined by createMeasureOptions |
5. What does not change
The measurement itself. The same thirty seconds, the same conditions, the same variables, the same result shape. A user comparing the two versions sees a different interface, not a different measurement.
Where you prepare the appearance does not change either: screens, colours and wording are prepared in the same console workshop for both versions. A customisation, though, is tied to one widget version — a v1 customisation is not applied by the v2 module, and its identifier on the v2 address brings nothing. Prepare a v2 customisation, and attach its identifier as Customisation describes.
Run the two side by side
Nothing prevents an account from serving v1 on one page and v2 on another, at the same time. That is the safest way to migrate: put v2 on a single journey, compare, then move the rest.v2 is in beta
Check with i-Virtual where it stands for your account before you plan a full migration.