Guides
Identity verification (Swiss ID card & passport)
A guided flow that proves a real, present person holds a valid Swiss identity document: explicit
consent, live document capture, and a short face video with unpredictable liveness challenges. The
widget only captures and uploads — every score and the final decision are computed on
the server, never on the visitor's device.
This is not the same as data capture. The
data-capture flow reads fields off a card and hands
them to your page. This flow runs a full identity-proofing pipeline (MRZ re-validation, face match,
liveness, a combined decision, an archived evidence chain) modelled on ETSI TS 119 461,
ISO/IEC 30107 and ICAO 9303, and results land in your dashboard's
Verifizierungen queue.
Quick start
<button type="button" data-jsscanner-verify-trigger
data-verify-type="swiss_passport">
Identität verifizieren
</button>
<script src="https://your-app.test/embed/YOUR_TOKEN.js"></script>
Use data-verify-type="swiss_id_card" for the identity card instead.
The flow
- Consent. The exact statement shown is stored verbatim plus a SHA-256 hash,
with a policy version, timestamp and IP (nDSG). Nothing starts before it is accepted.
- Document capture. Live camera only. The ID card is captured front then back;
the passport is a single data page. A blur check offers a retake before anything is sent. The
MRZ side is OCR'd in the browser as a hint — the server re-reads and re-checks every
check digit itself and never trusts the client's parse.
- Face video. The front camera records while the visitor performs a
server-decided sequence of challenges (turn head, blink) and reads a random 4-digit
code aloud. The order and the code are unpredictable to the client, which is what defeats a
pre-rendered deepfake, and the spoken code binds the recording to this specific session.
- Server processing. MRZ validation, document-face ↔ live-face match,
liveness/PAD analysis and speech verification, then a three-way decision:
approved, rejected, or manual review for anything in between.
- Result. The widget polls and shows the outcome. Grey-area cases go to the
review queue in your dashboard, where a trained reviewer decides with the full evidence in
front of them.
Customization
| Attribute | Effect |
data-verify-type | Required. swiss_id_card or swiss_passport. |
data-verify-accent | Highlight color for guide frames and buttons. |
data-verify-reference | Your own id (e.g. a signature-session id) stored on the verification for later correlation. |
data-jsscanner-onverify | Name of a global function called once with { uuid, status, decision } when the flow ends. |
Getting the result
document.addEventListener("jsscanner:verify", (event) => {
const { uuid, status, decision } = event.detail;
// status: "approved" | "rejected" | "awaiting_review" | "retry_requested"
// decision: "approved" | "rejected" | "manual_review" | null
});
Or name a callback:
<button type="button" data-jsscanner-verify-trigger
data-verify-type="swiss_id_card"
data-jsscanner-onverify="onVerified">
Verifizieren
</button>
<script>
function onVerified(result) {
if (result.status === "approved") { /* continue your flow */ }
}
</script>
Good to know
- The final
status the widget reports is not authoritative on its
own — a grey-area result is awaiting_review until a person
decides. Treat approved as the only "pass".
- Document images, the video and representative frames are stored encrypted, with a plaintext
SHA-256 per file, and deleted after the retention window — the decision, scores and
audit trail are kept as a tombstone.
- Each run counts against the project's monthly verification limit (separate from the scan
and session limits).
- The in-browser MRZ read uses Tesseract.js loaded from a pinned CDN version on first use. It is only a hint; the server's own validation is what counts.