Fingerprint reader client
Drives a USB fingerprint reader (the contactful path) via the SladeID companion application. See Use a USB fingerprint reader for the companion-app prerequisite, install links, and worked examples of enroll / verify / search.
import { createFingerprintReader, FingerprintReaderClient } from '@sladeid/slade-id-sdk';
const reader: FingerprintReaderClient = createFingerprintReader({
getToken: () => Promise.resolve('<bearer token>'),
});getToken is called on demand and again after a 401, so token rotation is transparent. Throwing inside it surfaces as a ReaderAuthError to the caller.
Methods
| Method | Returns | Notes |
|---|---|---|
reader.status | ReaderStatus | Synchronous snapshot from the last background poll. |
refreshStatus() | Promise<ReaderStatus> | Force an immediate /status fetch (e.g. before a device picker). |
enroll(input) | Promise<EnrollmentResult> | Register input.enrollee at input.position. |
verify(input) | Promise<VerificationResult> | 1:1 match against the claimed enrollee. |
search() | Promise<SearchResult> | 1:N identify against the enrolled corpus. No input. |
fetchEnrolled(enrollee) | Promise<EnrolledFingerprints> | List a subject’s enrolled positions. No hardware/device needed. |
testCapture(opts?) | Promise<TestCaptureResult> | Grab one frame; { normalized: true } for the preprocessed template. |
createEnrollmentSession(input) | EnrollmentSession | Event-driven wrapper around enroll. |
createVerificationSession(input) | VerificationSession | Event-driven wrapper around verify. |
createSearchSession() | SearchSession | Event-driven wrapper around search. |
dispose() | void | Stop polling, drop listeners. Subsequent actions throw. |
Inputs
EnrollmentInput and VerificationInput share the same shape:
| Field | Type | Required | Notes |
|---|---|---|---|
enrollee | string | yes | Subject identifier (e.g. beneficiary code). |
position | FingerPosition (0–10) | yes | 1–5 right hand, 6–10 left hand; 0 = minor / any. |
isMinor | boolean | no | Apply relaxed quality checks. |
attachments | Record<string, string> | no | Free-form metadata forwarded to the backend (e.g. encounter ids). |
search() takes no input — the capture itself is the query.
Results
interface EnrollmentResult {
template?: string; // base64 template, when the service returns one
verifiedPositions?: number[]; // positions the service confirmed
raw: Record<string, unknown>; // untouched server payload
}
interface VerificationResult {
matched: boolean; // false is a normal outcome, not an error
matchLogId?: string; // present when matched; persist for audit
verifiedPositions?: number[];
}
interface SearchResult {
found: boolean; // true only when an enrollee matched
enrollee?: string; // the matched subject id (single best match, not a ranked list)
raw: Record<string, unknown>; // any scoring fields (similarity, match_score) the backend returns
}
interface EnrolledFingerprints {
verified: number[]; // positions already enrolled — safe to skip on resume
nonVerified: number[]; // captured but not yet confirmed by the backend
total: number; // total fingerprint records on file
enrollmentStatus: 'fully_enrolled' | 'partially_enrolled';
notes?: string; // enrollment notes, when present
raw: Record<string, unknown>;
}fetchEnrolled(enrollee) is a metadata lookup — it selects no device and resolves even with no scanner attached. Use it to resume a paused multi-finger enrollment: skip the verified positions and prompt only for what’s missing.
ReaderStatus
interface ReaderStatus {
connected: boolean; // service reachable
isAuthed: boolean; // service accepted the token
devices: ReaderDevice[]; // scanners currently visible
workstationId?: string;
version?: string;
lastError?: HwsError; // most recent poll failure; cleared on next success
}The action methods auto-select the first device, so most integrators never read devices — it’s exposed for rendering a device picker.
Client events
connected, disconnected (HwsError), device-attached (ReaderDevice), device-detached (ReaderDevice), poll-failed (HwsError).
poll-failed fires whenever a background /status poll fails — including the very first poll before the client ever went connected. It is de-duped by error code, so a stuck failure (e.g. an empty token) emits once rather than every 3s. The same error is also available synchronously on reader.status.lastError, which is cleared on the next successful poll.
Session events
Sessions are single-shot: call start() once, cancel() to abort an in-flight request. All three emit ready (just before the request goes out — cue “place finger”) and scanning (once in flight — cue spinner), then a terminal event. The terminal events differ per session type — note that verification and search split the outcome so a non-match never looks like an error:
| Session | Success terminal | Non-match terminal | Failure terminal | Result type |
|---|---|---|---|---|
EnrollmentSession | captured | — | error | EnrollmentResult |
VerificationSession | captured | mismatch | error | VerificationResult |
SearchSession | found | not-found | error | SearchResult |
error is reserved for transport, auth, device, and server failures (HwsError subclasses). A biometric non-match is delivered through mismatch / not-found, not error.
Errors
All reader errors extend HwsError (which extends SladeIDError) and carry code plus an optional correlationId.
| Class | Code |
|---|---|
ConnectionError | READER_CONNECTION_FAILED |
TimeoutError | READER_TIMEOUT |
ReaderAuthError | READER_AUTH_FAILED |
DeviceUnavailableError | READER_DEVICE_UNAVAILABLE |
ReaderValidationError | READER_VALIDATION_FAILED |
ReaderServerError | READER_SERVER_ERROR |
BiometricMismatchError | READER_BIOMETRIC_MISMATCH |
BiometricMismatchError is thrown only by rejection-style helpers; the built-in verify() resolves with { matched: false } instead.