Web SDK guide

Soliton Identity Verification

Keep the user on your page.

The Web SDK runs the hosted verification flow in a full-screen overlay iframe instead of a redirect. Your backend integration (create, verdict, portrait) is identical to the server API guide; this page covers only what changes in the browser.

01

How it fits

Same verification, same one-time url; the SDK replaces the redirect hand-off with callbacks.

1

Create

Your backend calls POST /api/v1/verifications with your API key and liveness_tier, exactly as in redirect mode, and hands the returned url to your page.

2

Open

Your page calls open({ url, ... }). The SDK covers the viewport with an overlay iframe pointing at soliton.id.

3

Capture

The user completes the camera challenge inside the iframe, on soliton.id. Your page never touches video or biometrics.

4

Callback

onComplete fires with the verification id only. Your backend fetches the verdict server-to-server, exactly as in redirect mode.

Choose liveness on your server

Set liveness_tier when creating the verification: standard uses the camera with no sound or flashes; medium adds sound; high adds sound and light. Omitting the field preserves High for existing integrations. Standard requests camera permission only; Medium and High also request microphone access.

Create the selected tier (server only)
// Your backend only. Use sk_test_... for sandbox or sk_live_... for production.
const livenessTier = "standard"; // "standard" | "medium" | "high"; your server's policy
const response = await fetch("https://api.soliton.id/api/v1/verifications", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SOLITON_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    liveness_tier: livenessTier,
    client_reference_id: "user-8412",
  }),
});
if (!response.ok) throw new Error("Could not create verification");
const verification = await response.json();
// Store verification.verification_id, livenessTier and verification.environment
// against your authenticated user. Return only { url: verification.url } to the page.

All three tiers work with sandbox and production keys. The returned URL binds the choice for every capture and retake. Pass that URL to open(); there is no tier option or tier query parameter to set in the SDK. Existing SDK versions support every tier. Create a new verification if you need a different tier.

After onComplete, fetch the stored verification id from your backend and require the result's liveness_tier and environment to match your stored choice. Standard always returns partial_verification: false; Medium and High can return partial outcomes. See the full tier and verdict contract.

02

Register your origins

Embedding is off by default.

Ask us to register the exact origin of every page that runs the SDK and directly contains its Soliton iframe (scheme + host + port, e.g. https://app.example.com) for your Soliton account. This direct-parent page does not have to be the browser's top-level page. Register its origin, not its path: https://app.example.com and https://app.example.com:8443 are different origins, while /verify is not part of an origin. A verification link opened inside an iframe with any other direct-parent origin refuses to start and reports origin_not_allowed. Non-iframe opens (redirect mode) are unaffected: the same link works standalone or embedded. Redirect-domain registration and iframe-origin registration are separate; enabling one does not enable the other.

Origins are registered per environment, so tell us which one each is for. A link is checked against the origins registered for the environment of the key that created it: your staging page can embed sk_test_ links without being able to embed sk_live_ ones.

Allow the embed in your response headers

CSP (Content Security Policy) is the browser response header that controls which sources a page may load. If the page that runs the SDK sends a restrictive CSP, merge the Soliton source into its existing directives; do not replace the rest of your policy with these minimal examples. The frame-src source is required for every SDK distribution. The script-src source is needed only when loading the classic or ESM bundle directly from soliton.id, not when your app bundles the npm package. If any document above the Soliton iframe restricts camera or microphone in Permissions-Policy, every level in a nested frame chain must allow and delegate camera access for Standard, and camera plus microphone access for Medium and High, through each intervening iframe; an upstream denial cannot be overridden lower in the chain. The SDK already adds the matching allow attribute on the iframe it creates.

Minimal Soliton sources when bundling the npm package
# Production example for the npm package:
Content-Security-Policy: frame-src https://soliton.id
Permissions-Policy: camera=(self "https://soliton.id"), microphone=(self "https://soliton.id")
Minimal Soliton sources when loading a hosted SDK build
# Production example for the classic CDN script or hosted ESM:
Content-Security-Policy: frame-src https://soliton.id; script-src https://soliton.id
Permissions-Policy: camera=(self "https://soliton.id"), microphone=(self "https://soliton.id")
  • These examples use production. Add a staging origin to these directives only when Soliton has supplied its exact, working staging URL; include both origins only if the same page can open both environments.
  • For local customer-page testing, send us the exact origin, including its port (for example http://localhost:3000), for registration. It does not belong in frame-src or script-src while the iframe and hosted SDK still come from soliton.id.
  • Do not add Soliton to your page's frame-ancestors. That directive controls who may frame your page; frame-src controls what your page may frame.

03

Install

No runtime dependencies, no framework requirements.

npm (typed, ESM and CJS)
npm install @soliton-id/web-sdk
Or via script tag (same API, updates with every Soliton deploy)
<script src="https://soliton.id/sdk/v1/soliton-web-sdk.js"></script>
<!-- exposes the global SolitonVerify; SolitonVerify.open({...}) -->

A hosted ESM build is also available at https://soliton.id/sdk/v1/soliton-web-sdk.esm.js.

04

Open the flow

One call: open() renders the overlay and relays the hosted page’s lifecycle to your callbacks.

Open the flow
import { open } from "@soliton-id/web-sdk";

// Fetch this from YOUR backend, which called POST /api/v1/verifications.
const { url } = await fetch("/your-api/start-verification").then(r => r.json());

const handle = open({
  url,
  onComplete: ({ verificationId }) => {
    // Terminal state (pass OR final fail; not disclosed here). Treat this
    // purely as a "check now" signal: have your backend fetch the
    // verification id it stored at creation time, never this callback value.
  },
  onError: ({ code, message }) => {
    // link_expired | link_invalid | origin_not_allowed |
    // storage_unavailable | internal
  },
  onClose: () => {
    // User dismissed the flow before completion.
  },
});
// handle.close() dismisses the overlay programmatically.
  • The overlay is always full-viewport for every tier. High uses the whole screen for its light challenge; Standard and Medium keep the same layout without flashes. Do not clip the overlay into a widget.
  • redirect_url is ignored while embedded; you can set both and the same link works standalone (redirect) or embedded (callbacks).
  • handle.close() dismisses the overlay programmatically and fires onClose if no terminal state was reached.

05

Callbacks and failure modes

Three callbacks cover every runtime outcome.

onComplete

Fired once, when the flow reaches a terminal state (pass OR final fail; deliberately not distinguished here). Carries only the verificationId, never a verdict. The overlay closes itself right after.

onError

A flow-level error (codes below). The overlay stays open so the hosted page can show its own error card; call handle.close() if you want it gone.

onClose

The user dismissed the flow (the X button, the hosted Cancel, or handle.close()) before completion. Not fired after onComplete.

onError codes:

link_invalid

The URL is not a live verification link: malformed token, or the verification does not exist.

link_expired

The link's hour passed, or it was already used. Create a fresh verification and open the new url.

origin_not_allowed

The Soliton iframe's direct-parent origin (the page running the SDK) is not registered for your Soliton account. Ask us to register the exact origin (scheme + host + port).

storage_unavailable

The user's browser blocked the session storage the flow needs (fires before camera start, so no capture is lost).

internal

We could not prepare the verification. Safe to create a fresh link and retry.

06

Get the verdict

Server-to-server only, exactly like redirect mode.

Server API guide

Create verifications, poll the verdict, fetch the portrait, API errors, and the security model.

07

Why an iframe on soliton.id

The capture engine never runs on your origin.

Always our origin

Anti-injection and anti-deepfake defenses update with every Soliton deploy, without you shipping anything.

Verdicts stay server-side

postMessage payloads carry only the verification id. Any machine-readable pass/fail on your page would hand fraudsters an oracle.

Messages are triple-checked

The SDK only trusts events from the hosted origin, from its own iframe’s window, carrying the protocol marker; everything else is ignored.