@sekyro/web 0.1.0-rc.6 → 0.1.0-rc.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/LICENSE +3 -3
- package/README.md +4 -4
- package/dist/chunk-B45J4KC3.js +1 -0
- package/dist/chunk-BMA36W2W.js +1 -0
- package/dist/chunk-FPXL63KZ.js +1 -0
- package/dist/chunk-IYUOYINS.js +1 -0
- package/dist/chunk-P7ECRN47.js +1 -0
- package/dist/chunk-SXZCL5NO.js +1 -0
- package/dist/chunk-YOB4ZTYN.js +1 -0
- package/dist/chunk-YQBK6OIM.js +1 -0
- package/dist/client/index.cjs +1 -357
- package/dist/client/index.d.cts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +1 -15
- package/dist/core/index.cjs +1 -1639
- package/dist/core/index.js +1 -139
- package/dist/core/v3/detect-worker.cjs +1 -392
- package/dist/core/v3/detect-worker.js +1 -13
- package/dist/{create-verification-BKc9NFQj.d.cts → create-verification-C2JhOO6Y.d.cts} +94 -16
- package/dist/{create-verification-DjcMZHAQ.d.ts → create-verification-CGazwtAh.d.ts} +94 -16
- package/dist/detector-scanic-VQFFXKTL.js +1 -0
- package/dist/{hosted-client-DJXq_CB9.d.ts → hosted-client-kBeq-mnJ.d.cts} +164 -11
- package/dist/{hosted-client-DJXq_CB9.d.cts → hosted-client-kBeq-mnJ.d.ts} +164 -11
- package/dist/index.cjs +1 -3030
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -85
- package/dist/internal/index.cjs +1 -4439
- package/dist/internal/index.d.cts +10 -7
- package/dist/internal/index.d.ts +10 -7
- package/dist/internal/index.js +1 -657
- package/dist/popup/index.cjs +1 -76
- package/dist/popup/index.js +1 -11
- package/dist/scanic-ml-stub-KW7IZHUT.js +1 -0
- package/package.json +5 -8
- package/sbom.cdx.json +195 -0
- package/dist/chunk-MWNFSNMP.js +0 -52
- package/dist/chunk-MWNFSNMP.js.map +0 -1
- package/dist/chunk-NILNKLK6.js +0 -50
- package/dist/chunk-NILNKLK6.js.map +0 -1
- package/dist/chunk-NWF46H7K.js +0 -359
- package/dist/chunk-NWF46H7K.js.map +0 -1
- package/dist/chunk-R3SJQHYU.js +0 -1173
- package/dist/chunk-R3SJQHYU.js.map +0 -1
- package/dist/chunk-TG5IE552.js +0 -1128
- package/dist/chunk-TG5IE552.js.map +0 -1
- package/dist/chunk-WY6U7AIK.js +0 -193
- package/dist/chunk-WY6U7AIK.js.map +0 -1
- package/dist/chunk-YVFF4Q4G.js +0 -327
- package/dist/chunk-YVFF4Q4G.js.map +0 -1
- package/dist/client/index.cjs.map +0 -1
- package/dist/client/index.js.map +0 -1
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.js.map +0 -1
- package/dist/core/v3/detect-worker.cjs.map +0 -1
- package/dist/core/v3/detect-worker.js.map +0 -1
- package/dist/detector-scanic-4C6IEW2K.js +0 -787
- package/dist/detector-scanic-4C6IEW2K.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal/index.cjs.map +0 -1
- package/dist/internal/index.js.map +0 -1
- package/dist/popup/index.cjs.map +0 -1
- package/dist/popup/index.js.map +0 -1
- package/dist/scanic-ml-stub-S2OZ6HEF.js +0 -10
- package/dist/scanic-ml-stub-S2OZ6HEF.js.map +0 -1
|
@@ -6,10 +6,26 @@ type SessionOutcome = "approved" | "review" | "rejected";
|
|
|
6
6
|
type CheckState = "pending" | "input_required" | "processing" | "passed" | "failed" | "review" | "skipped";
|
|
7
7
|
/** The frozen check-type vocabulary (CHECK_TYPES). `phone_otp` joined in Task 13. */
|
|
8
8
|
type CheckType = "liveness" | "face_match" | "doc_ocr" | "population_registry" | "phone_otp";
|
|
9
|
-
type LivenessReason = "no_face" | "media_unreadable" | "clip_too_short" | "spoof_detected" | "review_band";
|
|
10
|
-
type PopulationReason = "nni_not_found" | "nni_invalid" | "registry_unavailable" | "result_from_cache";
|
|
11
|
-
type FaceMatchReason = "no_face_source" | "no_face_reference" | "low_quality_reference" | "below_threshold" | "review_band";
|
|
12
|
-
type DocReason = "side_missing" | "mrz_checksum_failed" | "unreadable" | "expired_document" | "authenticity_review" | "registry_mismatch";
|
|
9
|
+
type LivenessReason = "no_face" | "media_unreadable" | "clip_too_short" | "spoof_detected" | "review_band" | "liveness_inconclusive" | "mobile_required" | "uncalibrated_capture_channel";
|
|
10
|
+
type PopulationReason = "nni_not_found" | "nni_invalid" | "registry_unavailable" | "result_from_cache" | "nationality_not_allowed" | "underage" | "age_unverifiable" | "name_mismatch" | "birth_date_mismatch" | "nationality_mismatch";
|
|
11
|
+
type FaceMatchReason = "no_face_source" | "no_face_reference" | "low_quality_reference" | "below_threshold" | "review_band" | "match_inconclusive";
|
|
12
|
+
type DocReason = "side_missing" | "mrz_checksum_failed" | "unreadable" | "expired_document" | "authenticity_review" | "document_authenticity" | "registry_mismatch" | "document_nni_mismatch" | "document_type_not_accepted" | "nationality_not_allowed" | "underage" | "age_unverifiable" | "name_mismatch" | "birth_date_mismatch" | "nationality_mismatch" | "document_number_mismatch";
|
|
13
|
+
/**
|
|
14
|
+
* Profile v2 step 3 matching reasons: a field the partner sent at session
|
|
15
|
+
* create (`subject`) differs from the reference identity (document or
|
|
16
|
+
* registry). All four are final. `document_number_mismatch` is `doc_ocr` only.
|
|
17
|
+
*/
|
|
18
|
+
type MatchReason = "name_mismatch" | "birth_date_mismatch" | "nationality_mismatch" | "document_number_mismatch";
|
|
19
|
+
/**
|
|
20
|
+
* Profile v2 step 4 decision-mode reasons (the app decides automatically
|
|
21
|
+
* instead of sending the case to human review). These park the step at
|
|
22
|
+
* `input_required` while attempts remain: `liveness_inconclusive` /
|
|
23
|
+
* `match_inconclusive` (middle score band) and `document_authenticity`
|
|
24
|
+
* (retake the original card). `mobile_required` is kept for compatibility:
|
|
25
|
+
* the backend now answers a non-phone capture with a request-level
|
|
26
|
+
* `409 mobile_required` (continue on a phone; nothing used, not billed).
|
|
27
|
+
*/
|
|
28
|
+
type DecisionReason = "liveness_inconclusive" | "match_inconclusive" | "document_authenticity" | "mobile_required";
|
|
13
29
|
/**
|
|
14
30
|
* phone_otp reasons (Task 13). All transient — the caller fixes them by
|
|
15
31
|
* re-entering / resending: `otp_mismatch` (wrong or missing code),
|
|
@@ -23,6 +39,49 @@ type PhoneOtpReason = "otp_mismatch" | "otp_expired" | "otp_send_failed";
|
|
|
23
39
|
* and face_match both carry `review_band`.
|
|
24
40
|
*/
|
|
25
41
|
type CheckReason = LivenessReason | PopulationReason | FaceMatchReason | DocReason | PhoneOtpReason;
|
|
42
|
+
/**
|
|
43
|
+
* Session-level failure reasons: the session was stopped by a policy gate,
|
|
44
|
+
* not by a failed check. All are terminal (no retry on this session):
|
|
45
|
+
* - `location_not_allowed` / `anonymizer_blocked` (profile v2 step 2): the
|
|
46
|
+
* person's IP location is not allowed, or an anonymizer (VPN / proxy / Tor)
|
|
47
|
+
* was detected while the app blocks them;
|
|
48
|
+
* - `capture_tampered` (step 4): the device reported a tampered capture
|
|
49
|
+
* (virtual camera, hooking, emulator…) while the app blocks it;
|
|
50
|
+
* - `device_velocity_exceeded` (step 4): this device already verified too
|
|
51
|
+
* many different people while the app blocks it;
|
|
52
|
+
* - `device_cooldown` (step 4): this device had too many rejected sessions —
|
|
53
|
+
* {@link HostedSessionView.retryAfter} says when it may try again.
|
|
54
|
+
*/
|
|
55
|
+
type SessionFailureReason = "location_not_allowed" | "anonymizer_blocked" | "capture_tampered" | "device_velocity_exceeded" | "device_cooldown";
|
|
56
|
+
/**
|
|
57
|
+
* The profile-v2 policy reasons a session can end on: the two session-level
|
|
58
|
+
* reasons plus the failed check's policy reasons (`document_type_not_accepted`,
|
|
59
|
+
* `nationality_not_allowed`, `underage`, `age_unverifiable`,
|
|
60
|
+
* `document_nni_mismatch`).
|
|
61
|
+
*/
|
|
62
|
+
type PolicyFailureReason = SessionFailureReason | "document_type_not_accepted" | "nationality_not_allowed" | "underage" | "age_unverifiable" | "document_nni_mismatch";
|
|
63
|
+
/**
|
|
64
|
+
* How the person proves their identity when the app makes the document
|
|
65
|
+
* optional for registry nationals (profile v2 step 3): type their national
|
|
66
|
+
* number (NNI) and skip the document, or photograph their document.
|
|
67
|
+
*/
|
|
68
|
+
type IdentityMethod = "national_number" | "document_photo";
|
|
69
|
+
/**
|
|
70
|
+
* The identity-method choice of a session (`HostedSessionView.methodChoice`).
|
|
71
|
+
* While `available && selected === null` no check can be submitted (409
|
|
72
|
+
* `method_required`): the page asks first and sends the answer with
|
|
73
|
+
* `POST /hosted/method` ({@link HostedClient.selectMethod}).
|
|
74
|
+
*/
|
|
75
|
+
interface MethodChoiceView {
|
|
76
|
+
/** The session offers the choice (the app's document mode is `optional`). */
|
|
77
|
+
available: boolean;
|
|
78
|
+
/** The chosen method, or `null` while the person has not chosen. */
|
|
79
|
+
selected: IdentityMethod | null;
|
|
80
|
+
/** The methods to offer, in display order. */
|
|
81
|
+
options: IdentityMethod[];
|
|
82
|
+
}
|
|
83
|
+
/** A document type the backend reads today (kyc-docs-v2 vocabulary). */
|
|
84
|
+
type AcceptedDocumentType = "carte_identite" | "passport";
|
|
26
85
|
/**
|
|
27
86
|
* phone_otp DISPLAY-SAFE surface (Task 16). The backend stashes the OTP
|
|
28
87
|
* service `verifToken` on the check's persisted outputs (the doc_ocr partial
|
|
@@ -66,14 +125,34 @@ interface HostedStepView {
|
|
|
66
125
|
acceptedSides?: ("recto" | "verso")[];
|
|
67
126
|
/** phone_otp only: the display-safe OTP send/config surface (never the verifToken). */
|
|
68
127
|
otp?: HostedOtpView;
|
|
128
|
+
/**
|
|
129
|
+
* The check failed after using all of its attempts — no retry is left.
|
|
130
|
+
* Absent on older APIs (treat absent as `false`).
|
|
131
|
+
*/
|
|
132
|
+
attemptsExhausted?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* population_registry only: the session is not bound to an NNI, but the
|
|
135
|
+
* document step already read a valid one — the page submits the registry
|
|
136
|
+
* check with an EMPTY input (no NNI field) and the backend uses the
|
|
137
|
+
* document's number. Absent on older APIs.
|
|
138
|
+
*/
|
|
139
|
+
nniFromDocument?: boolean;
|
|
140
|
+
/**
|
|
141
|
+
* liveness / face_match (profile v2 step 4, decision mode `automatic`): the
|
|
142
|
+
* capture must come from a phone — the liveness band is calibrated on
|
|
143
|
+
* mobile captures only. A desktop page hands the person off to their phone
|
|
144
|
+
* instead of capturing (a desktop submission answers `mobile_required`).
|
|
145
|
+
* Absent on older APIs and in `review` mode.
|
|
146
|
+
*/
|
|
147
|
+
requiresMobile?: boolean;
|
|
69
148
|
}
|
|
70
|
-
/** A locale the Sekyro-hosted voice clips are recorded in. */
|
|
149
|
+
/** A locale the Sekyro AI-hosted voice clips are recorded in. */
|
|
71
150
|
type HostedVoiceLocale = "ar" | "fr" | "en";
|
|
72
151
|
/**
|
|
73
|
-
* The session's Sekyro-hosted voice-guidance clips (the active clip set).
|
|
152
|
+
* The session's Sekyro AI-hosted voice-guidance clips (the active clip set).
|
|
74
153
|
*
|
|
75
154
|
* `clips[locale]` maps a voice key (`fcv3.*`, the `@sekyro/react` voice-key
|
|
76
|
-
* namespace) to an ABSOLUTE URL on the Sekyro assets host
|
|
155
|
+
* namespace) to an ABSOLUTE URL on the Sekyro AI assets host
|
|
77
156
|
* (`https://assets.sekyro.ai/voice/<setId>/<locale>/<name>.mp3`; staging:
|
|
78
157
|
* `assets-staging.sekyro.ai`). URLs are immutable per `setId`. A locale or key
|
|
79
158
|
* may be missing — the player falls back to the device voice for it.
|
|
@@ -89,6 +168,25 @@ interface HostedVoiceView {
|
|
|
89
168
|
readonly [L in HostedVoiceLocale]?: Readonly<Record<string, string>>;
|
|
90
169
|
};
|
|
91
170
|
}
|
|
171
|
+
/** A branding colour key (each value is `#rrggbb`). */
|
|
172
|
+
type HostedBrandingColorKey = "brand" | "brandFg" | "bg" | "surface" | "fg" | "fgMuted" | "danger" | "success" | "warning";
|
|
173
|
+
/**
|
|
174
|
+
* The org / app branding of a session (`HostedSessionView.branding`): only the
|
|
175
|
+
* fields set in the dashboard, merged per field (app over org). A field the
|
|
176
|
+
* org and app left unset is ABSENT — keep your default for it; the whole
|
|
177
|
+
* object is absent when nothing is set. `logoUrl` is a short-lived signed URL
|
|
178
|
+
* (15 minutes).
|
|
179
|
+
*/
|
|
180
|
+
interface HostedBranding {
|
|
181
|
+
/** Signed URL of the logo (PNG or sanitized SVG); absent when none is set. */
|
|
182
|
+
logoUrl?: string;
|
|
183
|
+
/** Colours as `#rrggbb`. */
|
|
184
|
+
colors?: Partial<Record<HostedBrandingColorKey, string>>;
|
|
185
|
+
/** Corner radius step. */
|
|
186
|
+
radius?: "none" | "sm" | "md" | "lg" | "full";
|
|
187
|
+
/** Font choice (system fallbacks when the font is not installed). */
|
|
188
|
+
fontFamily?: "system" | "inter" | "ibm-plex-sans-arabic" | "noto-sans-arabic" | "roboto";
|
|
189
|
+
}
|
|
92
190
|
/**
|
|
93
191
|
* Token-addressed hosted-page bootstrap (`GET /hosted/session`). A separate
|
|
94
192
|
* shape from the partner `SessionView`: `sessionId` instead of `id`,
|
|
@@ -133,11 +231,40 @@ interface HostedSessionView {
|
|
|
133
231
|
*/
|
|
134
232
|
nniBound?: boolean;
|
|
135
233
|
/**
|
|
136
|
-
* The Sekyro-hosted voice-guidance clips of this session, or `null` when no
|
|
234
|
+
* The Sekyro AI-hosted voice-guidance clips of this session, or `null` when no
|
|
137
235
|
* clip set is active (the device voice is used). Optional: older APIs omit
|
|
138
236
|
* the field — treat absent like `null`.
|
|
139
237
|
*/
|
|
140
238
|
voice?: HostedVoiceView | null;
|
|
239
|
+
/**
|
|
240
|
+
* Why the session ended, when a profile-v2 policy stopped it: a
|
|
241
|
+
* {@link SessionFailureReason}, or the failed check's reason when it is a
|
|
242
|
+
* policy reason (eligibility, or `document_nni_mismatch`). Absent otherwise
|
|
243
|
+
* (and on older APIs).
|
|
244
|
+
*/
|
|
245
|
+
failureReason?: PolicyFailureReason;
|
|
246
|
+
/**
|
|
247
|
+
* The document types the person may pick (resolved for issuer `MR`). Absent
|
|
248
|
+
* on older APIs — treat absent as every type (`carte_identite`, `passport`).
|
|
249
|
+
*/
|
|
250
|
+
acceptedDocumentTypes?: AcceptedDocumentType[];
|
|
251
|
+
/**
|
|
252
|
+
* The identity-method choice (profile v2 step 3). Absent when the session
|
|
253
|
+
* has no choice (and on older APIs). While `available && selected === null`
|
|
254
|
+
* every check submission answers 409 `method_required`.
|
|
255
|
+
*/
|
|
256
|
+
methodChoice?: MethodChoiceView;
|
|
257
|
+
/**
|
|
258
|
+
* ISO-8601 time from which this device may verify again — set with
|
|
259
|
+
* `failureReason: 'device_cooldown'` (profile v2 step 4). Absent otherwise
|
|
260
|
+
* (and on older APIs).
|
|
261
|
+
*/
|
|
262
|
+
retryAfter?: string;
|
|
263
|
+
/**
|
|
264
|
+
* The org / app branding (settings spec §5.1). Absent on older APIs — keep
|
|
265
|
+
* the default theme. Map it with `brandingToTheme()` from `@sekyro/react`.
|
|
266
|
+
*/
|
|
267
|
+
branding?: HostedBranding;
|
|
141
268
|
}
|
|
142
269
|
/** `POST /hosted/consent` result — 201 on first record, 200 `{alreadyRecorded: true}` on replay. */
|
|
143
270
|
interface RecordConsentResult {
|
|
@@ -151,6 +278,8 @@ interface SessionStepView {
|
|
|
151
278
|
state: CheckState;
|
|
152
279
|
reason?: CheckReason;
|
|
153
280
|
calibratedScore?: number;
|
|
281
|
+
/** The check failed after using all of its attempts. Absent on older APIs. */
|
|
282
|
+
attemptsExhausted?: boolean;
|
|
154
283
|
}
|
|
155
284
|
/**
|
|
156
285
|
* Client-facing session projection returned by
|
|
@@ -160,6 +289,8 @@ interface SessionView {
|
|
|
160
289
|
id: string;
|
|
161
290
|
status: SessionStatus;
|
|
162
291
|
outcome: SessionOutcome | null;
|
|
292
|
+
/** Profile-v2 policy stop reason; absent on ordinary failures and older APIs. */
|
|
293
|
+
failureReason?: PolicyFailureReason;
|
|
163
294
|
steps: SessionStepView[];
|
|
164
295
|
/** ISO-8601 timestamp (Date on the server, string on the wire). */
|
|
165
296
|
expiresAt: string;
|
|
@@ -209,7 +340,14 @@ interface AttestationInput {
|
|
|
209
340
|
/** The Turnstile site key the token was minted with (selects the verifying secret). */
|
|
210
341
|
siteKey?: string;
|
|
211
342
|
}
|
|
212
|
-
/**
|
|
343
|
+
/**
|
|
344
|
+
* liveness executor input: a promoted `clips/…` key + optional attestation.
|
|
345
|
+
* `faceMatch` (v2 latency §3.2) carries the already-promoted `doc-images/…`
|
|
346
|
+
* keyframe so the backend can run face_match IN THE SAME submission when its
|
|
347
|
+
* bundle flag is on and `face_match` is the next pending step. Backends that
|
|
348
|
+
* do not bundle ignore it (the input bag is an open record) — the SDK then
|
|
349
|
+
* submits `face_match` itself with the same key (see liveness-flow.ts).
|
|
350
|
+
*/
|
|
213
351
|
interface LivenessInput {
|
|
214
352
|
clipKey: string;
|
|
215
353
|
attestation?: AttestationInput;
|
|
@@ -221,6 +359,10 @@ interface LivenessInput {
|
|
|
221
359
|
/** Capture metadata (engine, dimensions, clip length, face-capture timing)
|
|
222
360
|
* — the backend's allowlisted, non-PII `capture` block (session insights). */
|
|
223
361
|
capture?: LivenessCaptureInput;
|
|
362
|
+
/** The promoted face_match probe key, for a bundled liveness + face_match run. */
|
|
363
|
+
faceMatch?: {
|
|
364
|
+
imageKey: string;
|
|
365
|
+
};
|
|
224
366
|
}
|
|
225
367
|
/** `LivenessInput.capture`: what the SDK's `captured` event measured. All ms are whole, ≥ 0. */
|
|
226
368
|
interface LivenessCaptureInput {
|
|
@@ -241,7 +383,10 @@ interface LivenessCaptureInput {
|
|
|
241
383
|
interface PopulationInput {
|
|
242
384
|
nni?: string;
|
|
243
385
|
}
|
|
244
|
-
/**
|
|
386
|
+
/**
|
|
387
|
+
* face_match executor input: a promoted `doc-images/…` key (the source selfie frame).
|
|
388
|
+
* The same key may already have ridden along as {@link LivenessInput.faceMatch}.
|
|
389
|
+
*/
|
|
245
390
|
interface FaceMatchInput {
|
|
246
391
|
imageKey: string;
|
|
247
392
|
}
|
|
@@ -348,6 +493,7 @@ interface HostedClientOptions {
|
|
|
348
493
|
*
|
|
349
494
|
* GET /api/v2/hosted/session → {@link getSession}
|
|
350
495
|
* POST /api/v2/hosted/consent → {@link recordConsent}
|
|
496
|
+
* POST /api/v2/hosted/method → {@link selectMethod}
|
|
351
497
|
* POST /api/v2/sessions/:id/media → {@link presign}
|
|
352
498
|
* POST /api/v2/sessions/:id/media/complete → {@link completeMedia}
|
|
353
499
|
* POST /api/v2/sessions/:id/checks/:checkType → {@link submitCheck}
|
|
@@ -386,6 +532,13 @@ declare class HostedClient {
|
|
|
386
532
|
* submission answer 409 `consent_required`.
|
|
387
533
|
*/
|
|
388
534
|
recordConsent(locale: ConsentLocale): Promise<RecordConsentResult>;
|
|
535
|
+
/**
|
|
536
|
+
* Send the person's identity method (profile v2 step 3) when the view offers
|
|
537
|
+
* a choice (`methodChoice.available`). Returns the updated hosted view —
|
|
538
|
+
* continue from it. 409 `method_locked` when the method can no longer
|
|
539
|
+
* change (a check of the chosen method was already submitted).
|
|
540
|
+
*/
|
|
541
|
+
selectMethod(method: IdentityMethod): Promise<HostedSessionView>;
|
|
389
542
|
/**
|
|
390
543
|
* Presign a Spaces PUT for a new pending object. `contentType` is stripped of
|
|
391
544
|
* MIME parameters defensively (the allowlist holds bare types; the engine's
|
|
@@ -446,4 +599,4 @@ declare class HostedClient {
|
|
|
446
599
|
private api;
|
|
447
600
|
}
|
|
448
601
|
|
|
449
|
-
export { type
|
|
602
|
+
export { type AcceptedDocumentType as A, type MediaCheckType as B, type CheckReason as C, type DecisionReason as D, type MediaKind as E, type FaceMatchReason as F, type PhoneOtpInput as G, type HostedBranding as H, type IdentityMethod as I, type PopulationInput as J, type PresignRequest as K, type LivenessReason as L, type MatchReason as M, type PresignUploadResult as N, type SessionStepView as O, type PhoneOtpReason as P, type SubmitCheckBody as Q, type RecordConsentResult as R, type SessionFailureReason as S, type SubmitOptions as T, type UploadMedia as U, stripMimeParams as V, type CheckState as a, type CheckType as b, type DocReason as c, type HostedBrandingColorKey as d, type HostedOtpView as e, type HostedSessionView as f, type HostedStepView as g, type HostedVoiceLocale as h, type HostedVoiceView as i, type MethodChoiceView as j, type PolicyFailureReason as k, type PopulationReason as l, type SessionOutcome as m, type SessionStatus as n, HostedClient as o, type AttestationInput as p, type SessionView as q, type CheckInputByType as r, type CompleteMediaResult as s, type ConsentLocale as t, type DocOcrInput as u, type DocSide as v, type FaceMatchInput as w, type HostedClientOptions as x, type LivenessCaptureInput as y, type LivenessInput as z };
|
|
@@ -6,10 +6,26 @@ type SessionOutcome = "approved" | "review" | "rejected";
|
|
|
6
6
|
type CheckState = "pending" | "input_required" | "processing" | "passed" | "failed" | "review" | "skipped";
|
|
7
7
|
/** The frozen check-type vocabulary (CHECK_TYPES). `phone_otp` joined in Task 13. */
|
|
8
8
|
type CheckType = "liveness" | "face_match" | "doc_ocr" | "population_registry" | "phone_otp";
|
|
9
|
-
type LivenessReason = "no_face" | "media_unreadable" | "clip_too_short" | "spoof_detected" | "review_band";
|
|
10
|
-
type PopulationReason = "nni_not_found" | "nni_invalid" | "registry_unavailable" | "result_from_cache";
|
|
11
|
-
type FaceMatchReason = "no_face_source" | "no_face_reference" | "low_quality_reference" | "below_threshold" | "review_band";
|
|
12
|
-
type DocReason = "side_missing" | "mrz_checksum_failed" | "unreadable" | "expired_document" | "authenticity_review" | "registry_mismatch";
|
|
9
|
+
type LivenessReason = "no_face" | "media_unreadable" | "clip_too_short" | "spoof_detected" | "review_band" | "liveness_inconclusive" | "mobile_required" | "uncalibrated_capture_channel";
|
|
10
|
+
type PopulationReason = "nni_not_found" | "nni_invalid" | "registry_unavailable" | "result_from_cache" | "nationality_not_allowed" | "underage" | "age_unverifiable" | "name_mismatch" | "birth_date_mismatch" | "nationality_mismatch";
|
|
11
|
+
type FaceMatchReason = "no_face_source" | "no_face_reference" | "low_quality_reference" | "below_threshold" | "review_band" | "match_inconclusive";
|
|
12
|
+
type DocReason = "side_missing" | "mrz_checksum_failed" | "unreadable" | "expired_document" | "authenticity_review" | "document_authenticity" | "registry_mismatch" | "document_nni_mismatch" | "document_type_not_accepted" | "nationality_not_allowed" | "underage" | "age_unverifiable" | "name_mismatch" | "birth_date_mismatch" | "nationality_mismatch" | "document_number_mismatch";
|
|
13
|
+
/**
|
|
14
|
+
* Profile v2 step 3 matching reasons: a field the partner sent at session
|
|
15
|
+
* create (`subject`) differs from the reference identity (document or
|
|
16
|
+
* registry). All four are final. `document_number_mismatch` is `doc_ocr` only.
|
|
17
|
+
*/
|
|
18
|
+
type MatchReason = "name_mismatch" | "birth_date_mismatch" | "nationality_mismatch" | "document_number_mismatch";
|
|
19
|
+
/**
|
|
20
|
+
* Profile v2 step 4 decision-mode reasons (the app decides automatically
|
|
21
|
+
* instead of sending the case to human review). These park the step at
|
|
22
|
+
* `input_required` while attempts remain: `liveness_inconclusive` /
|
|
23
|
+
* `match_inconclusive` (middle score band) and `document_authenticity`
|
|
24
|
+
* (retake the original card). `mobile_required` is kept for compatibility:
|
|
25
|
+
* the backend now answers a non-phone capture with a request-level
|
|
26
|
+
* `409 mobile_required` (continue on a phone; nothing used, not billed).
|
|
27
|
+
*/
|
|
28
|
+
type DecisionReason = "liveness_inconclusive" | "match_inconclusive" | "document_authenticity" | "mobile_required";
|
|
13
29
|
/**
|
|
14
30
|
* phone_otp reasons (Task 13). All transient — the caller fixes them by
|
|
15
31
|
* re-entering / resending: `otp_mismatch` (wrong or missing code),
|
|
@@ -23,6 +39,49 @@ type PhoneOtpReason = "otp_mismatch" | "otp_expired" | "otp_send_failed";
|
|
|
23
39
|
* and face_match both carry `review_band`.
|
|
24
40
|
*/
|
|
25
41
|
type CheckReason = LivenessReason | PopulationReason | FaceMatchReason | DocReason | PhoneOtpReason;
|
|
42
|
+
/**
|
|
43
|
+
* Session-level failure reasons: the session was stopped by a policy gate,
|
|
44
|
+
* not by a failed check. All are terminal (no retry on this session):
|
|
45
|
+
* - `location_not_allowed` / `anonymizer_blocked` (profile v2 step 2): the
|
|
46
|
+
* person's IP location is not allowed, or an anonymizer (VPN / proxy / Tor)
|
|
47
|
+
* was detected while the app blocks them;
|
|
48
|
+
* - `capture_tampered` (step 4): the device reported a tampered capture
|
|
49
|
+
* (virtual camera, hooking, emulator…) while the app blocks it;
|
|
50
|
+
* - `device_velocity_exceeded` (step 4): this device already verified too
|
|
51
|
+
* many different people while the app blocks it;
|
|
52
|
+
* - `device_cooldown` (step 4): this device had too many rejected sessions —
|
|
53
|
+
* {@link HostedSessionView.retryAfter} says when it may try again.
|
|
54
|
+
*/
|
|
55
|
+
type SessionFailureReason = "location_not_allowed" | "anonymizer_blocked" | "capture_tampered" | "device_velocity_exceeded" | "device_cooldown";
|
|
56
|
+
/**
|
|
57
|
+
* The profile-v2 policy reasons a session can end on: the two session-level
|
|
58
|
+
* reasons plus the failed check's policy reasons (`document_type_not_accepted`,
|
|
59
|
+
* `nationality_not_allowed`, `underage`, `age_unverifiable`,
|
|
60
|
+
* `document_nni_mismatch`).
|
|
61
|
+
*/
|
|
62
|
+
type PolicyFailureReason = SessionFailureReason | "document_type_not_accepted" | "nationality_not_allowed" | "underage" | "age_unverifiable" | "document_nni_mismatch";
|
|
63
|
+
/**
|
|
64
|
+
* How the person proves their identity when the app makes the document
|
|
65
|
+
* optional for registry nationals (profile v2 step 3): type their national
|
|
66
|
+
* number (NNI) and skip the document, or photograph their document.
|
|
67
|
+
*/
|
|
68
|
+
type IdentityMethod = "national_number" | "document_photo";
|
|
69
|
+
/**
|
|
70
|
+
* The identity-method choice of a session (`HostedSessionView.methodChoice`).
|
|
71
|
+
* While `available && selected === null` no check can be submitted (409
|
|
72
|
+
* `method_required`): the page asks first and sends the answer with
|
|
73
|
+
* `POST /hosted/method` ({@link HostedClient.selectMethod}).
|
|
74
|
+
*/
|
|
75
|
+
interface MethodChoiceView {
|
|
76
|
+
/** The session offers the choice (the app's document mode is `optional`). */
|
|
77
|
+
available: boolean;
|
|
78
|
+
/** The chosen method, or `null` while the person has not chosen. */
|
|
79
|
+
selected: IdentityMethod | null;
|
|
80
|
+
/** The methods to offer, in display order. */
|
|
81
|
+
options: IdentityMethod[];
|
|
82
|
+
}
|
|
83
|
+
/** A document type the backend reads today (kyc-docs-v2 vocabulary). */
|
|
84
|
+
type AcceptedDocumentType = "carte_identite" | "passport";
|
|
26
85
|
/**
|
|
27
86
|
* phone_otp DISPLAY-SAFE surface (Task 16). The backend stashes the OTP
|
|
28
87
|
* service `verifToken` on the check's persisted outputs (the doc_ocr partial
|
|
@@ -66,14 +125,34 @@ interface HostedStepView {
|
|
|
66
125
|
acceptedSides?: ("recto" | "verso")[];
|
|
67
126
|
/** phone_otp only: the display-safe OTP send/config surface (never the verifToken). */
|
|
68
127
|
otp?: HostedOtpView;
|
|
128
|
+
/**
|
|
129
|
+
* The check failed after using all of its attempts — no retry is left.
|
|
130
|
+
* Absent on older APIs (treat absent as `false`).
|
|
131
|
+
*/
|
|
132
|
+
attemptsExhausted?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* population_registry only: the session is not bound to an NNI, but the
|
|
135
|
+
* document step already read a valid one — the page submits the registry
|
|
136
|
+
* check with an EMPTY input (no NNI field) and the backend uses the
|
|
137
|
+
* document's number. Absent on older APIs.
|
|
138
|
+
*/
|
|
139
|
+
nniFromDocument?: boolean;
|
|
140
|
+
/**
|
|
141
|
+
* liveness / face_match (profile v2 step 4, decision mode `automatic`): the
|
|
142
|
+
* capture must come from a phone — the liveness band is calibrated on
|
|
143
|
+
* mobile captures only. A desktop page hands the person off to their phone
|
|
144
|
+
* instead of capturing (a desktop submission answers `mobile_required`).
|
|
145
|
+
* Absent on older APIs and in `review` mode.
|
|
146
|
+
*/
|
|
147
|
+
requiresMobile?: boolean;
|
|
69
148
|
}
|
|
70
|
-
/** A locale the Sekyro-hosted voice clips are recorded in. */
|
|
149
|
+
/** A locale the Sekyro AI-hosted voice clips are recorded in. */
|
|
71
150
|
type HostedVoiceLocale = "ar" | "fr" | "en";
|
|
72
151
|
/**
|
|
73
|
-
* The session's Sekyro-hosted voice-guidance clips (the active clip set).
|
|
152
|
+
* The session's Sekyro AI-hosted voice-guidance clips (the active clip set).
|
|
74
153
|
*
|
|
75
154
|
* `clips[locale]` maps a voice key (`fcv3.*`, the `@sekyro/react` voice-key
|
|
76
|
-
* namespace) to an ABSOLUTE URL on the Sekyro assets host
|
|
155
|
+
* namespace) to an ABSOLUTE URL on the Sekyro AI assets host
|
|
77
156
|
* (`https://assets.sekyro.ai/voice/<setId>/<locale>/<name>.mp3`; staging:
|
|
78
157
|
* `assets-staging.sekyro.ai`). URLs are immutable per `setId`. A locale or key
|
|
79
158
|
* may be missing — the player falls back to the device voice for it.
|
|
@@ -89,6 +168,25 @@ interface HostedVoiceView {
|
|
|
89
168
|
readonly [L in HostedVoiceLocale]?: Readonly<Record<string, string>>;
|
|
90
169
|
};
|
|
91
170
|
}
|
|
171
|
+
/** A branding colour key (each value is `#rrggbb`). */
|
|
172
|
+
type HostedBrandingColorKey = "brand" | "brandFg" | "bg" | "surface" | "fg" | "fgMuted" | "danger" | "success" | "warning";
|
|
173
|
+
/**
|
|
174
|
+
* The org / app branding of a session (`HostedSessionView.branding`): only the
|
|
175
|
+
* fields set in the dashboard, merged per field (app over org). A field the
|
|
176
|
+
* org and app left unset is ABSENT — keep your default for it; the whole
|
|
177
|
+
* object is absent when nothing is set. `logoUrl` is a short-lived signed URL
|
|
178
|
+
* (15 minutes).
|
|
179
|
+
*/
|
|
180
|
+
interface HostedBranding {
|
|
181
|
+
/** Signed URL of the logo (PNG or sanitized SVG); absent when none is set. */
|
|
182
|
+
logoUrl?: string;
|
|
183
|
+
/** Colours as `#rrggbb`. */
|
|
184
|
+
colors?: Partial<Record<HostedBrandingColorKey, string>>;
|
|
185
|
+
/** Corner radius step. */
|
|
186
|
+
radius?: "none" | "sm" | "md" | "lg" | "full";
|
|
187
|
+
/** Font choice (system fallbacks when the font is not installed). */
|
|
188
|
+
fontFamily?: "system" | "inter" | "ibm-plex-sans-arabic" | "noto-sans-arabic" | "roboto";
|
|
189
|
+
}
|
|
92
190
|
/**
|
|
93
191
|
* Token-addressed hosted-page bootstrap (`GET /hosted/session`). A separate
|
|
94
192
|
* shape from the partner `SessionView`: `sessionId` instead of `id`,
|
|
@@ -133,11 +231,40 @@ interface HostedSessionView {
|
|
|
133
231
|
*/
|
|
134
232
|
nniBound?: boolean;
|
|
135
233
|
/**
|
|
136
|
-
* The Sekyro-hosted voice-guidance clips of this session, or `null` when no
|
|
234
|
+
* The Sekyro AI-hosted voice-guidance clips of this session, or `null` when no
|
|
137
235
|
* clip set is active (the device voice is used). Optional: older APIs omit
|
|
138
236
|
* the field — treat absent like `null`.
|
|
139
237
|
*/
|
|
140
238
|
voice?: HostedVoiceView | null;
|
|
239
|
+
/**
|
|
240
|
+
* Why the session ended, when a profile-v2 policy stopped it: a
|
|
241
|
+
* {@link SessionFailureReason}, or the failed check's reason when it is a
|
|
242
|
+
* policy reason (eligibility, or `document_nni_mismatch`). Absent otherwise
|
|
243
|
+
* (and on older APIs).
|
|
244
|
+
*/
|
|
245
|
+
failureReason?: PolicyFailureReason;
|
|
246
|
+
/**
|
|
247
|
+
* The document types the person may pick (resolved for issuer `MR`). Absent
|
|
248
|
+
* on older APIs — treat absent as every type (`carte_identite`, `passport`).
|
|
249
|
+
*/
|
|
250
|
+
acceptedDocumentTypes?: AcceptedDocumentType[];
|
|
251
|
+
/**
|
|
252
|
+
* The identity-method choice (profile v2 step 3). Absent when the session
|
|
253
|
+
* has no choice (and on older APIs). While `available && selected === null`
|
|
254
|
+
* every check submission answers 409 `method_required`.
|
|
255
|
+
*/
|
|
256
|
+
methodChoice?: MethodChoiceView;
|
|
257
|
+
/**
|
|
258
|
+
* ISO-8601 time from which this device may verify again — set with
|
|
259
|
+
* `failureReason: 'device_cooldown'` (profile v2 step 4). Absent otherwise
|
|
260
|
+
* (and on older APIs).
|
|
261
|
+
*/
|
|
262
|
+
retryAfter?: string;
|
|
263
|
+
/**
|
|
264
|
+
* The org / app branding (settings spec §5.1). Absent on older APIs — keep
|
|
265
|
+
* the default theme. Map it with `brandingToTheme()` from `@sekyro/react`.
|
|
266
|
+
*/
|
|
267
|
+
branding?: HostedBranding;
|
|
141
268
|
}
|
|
142
269
|
/** `POST /hosted/consent` result — 201 on first record, 200 `{alreadyRecorded: true}` on replay. */
|
|
143
270
|
interface RecordConsentResult {
|
|
@@ -151,6 +278,8 @@ interface SessionStepView {
|
|
|
151
278
|
state: CheckState;
|
|
152
279
|
reason?: CheckReason;
|
|
153
280
|
calibratedScore?: number;
|
|
281
|
+
/** The check failed after using all of its attempts. Absent on older APIs. */
|
|
282
|
+
attemptsExhausted?: boolean;
|
|
154
283
|
}
|
|
155
284
|
/**
|
|
156
285
|
* Client-facing session projection returned by
|
|
@@ -160,6 +289,8 @@ interface SessionView {
|
|
|
160
289
|
id: string;
|
|
161
290
|
status: SessionStatus;
|
|
162
291
|
outcome: SessionOutcome | null;
|
|
292
|
+
/** Profile-v2 policy stop reason; absent on ordinary failures and older APIs. */
|
|
293
|
+
failureReason?: PolicyFailureReason;
|
|
163
294
|
steps: SessionStepView[];
|
|
164
295
|
/** ISO-8601 timestamp (Date on the server, string on the wire). */
|
|
165
296
|
expiresAt: string;
|
|
@@ -209,7 +340,14 @@ interface AttestationInput {
|
|
|
209
340
|
/** The Turnstile site key the token was minted with (selects the verifying secret). */
|
|
210
341
|
siteKey?: string;
|
|
211
342
|
}
|
|
212
|
-
/**
|
|
343
|
+
/**
|
|
344
|
+
* liveness executor input: a promoted `clips/…` key + optional attestation.
|
|
345
|
+
* `faceMatch` (v2 latency §3.2) carries the already-promoted `doc-images/…`
|
|
346
|
+
* keyframe so the backend can run face_match IN THE SAME submission when its
|
|
347
|
+
* bundle flag is on and `face_match` is the next pending step. Backends that
|
|
348
|
+
* do not bundle ignore it (the input bag is an open record) — the SDK then
|
|
349
|
+
* submits `face_match` itself with the same key (see liveness-flow.ts).
|
|
350
|
+
*/
|
|
213
351
|
interface LivenessInput {
|
|
214
352
|
clipKey: string;
|
|
215
353
|
attestation?: AttestationInput;
|
|
@@ -221,6 +359,10 @@ interface LivenessInput {
|
|
|
221
359
|
/** Capture metadata (engine, dimensions, clip length, face-capture timing)
|
|
222
360
|
* — the backend's allowlisted, non-PII `capture` block (session insights). */
|
|
223
361
|
capture?: LivenessCaptureInput;
|
|
362
|
+
/** The promoted face_match probe key, for a bundled liveness + face_match run. */
|
|
363
|
+
faceMatch?: {
|
|
364
|
+
imageKey: string;
|
|
365
|
+
};
|
|
224
366
|
}
|
|
225
367
|
/** `LivenessInput.capture`: what the SDK's `captured` event measured. All ms are whole, ≥ 0. */
|
|
226
368
|
interface LivenessCaptureInput {
|
|
@@ -241,7 +383,10 @@ interface LivenessCaptureInput {
|
|
|
241
383
|
interface PopulationInput {
|
|
242
384
|
nni?: string;
|
|
243
385
|
}
|
|
244
|
-
/**
|
|
386
|
+
/**
|
|
387
|
+
* face_match executor input: a promoted `doc-images/…` key (the source selfie frame).
|
|
388
|
+
* The same key may already have ridden along as {@link LivenessInput.faceMatch}.
|
|
389
|
+
*/
|
|
245
390
|
interface FaceMatchInput {
|
|
246
391
|
imageKey: string;
|
|
247
392
|
}
|
|
@@ -348,6 +493,7 @@ interface HostedClientOptions {
|
|
|
348
493
|
*
|
|
349
494
|
* GET /api/v2/hosted/session → {@link getSession}
|
|
350
495
|
* POST /api/v2/hosted/consent → {@link recordConsent}
|
|
496
|
+
* POST /api/v2/hosted/method → {@link selectMethod}
|
|
351
497
|
* POST /api/v2/sessions/:id/media → {@link presign}
|
|
352
498
|
* POST /api/v2/sessions/:id/media/complete → {@link completeMedia}
|
|
353
499
|
* POST /api/v2/sessions/:id/checks/:checkType → {@link submitCheck}
|
|
@@ -386,6 +532,13 @@ declare class HostedClient {
|
|
|
386
532
|
* submission answer 409 `consent_required`.
|
|
387
533
|
*/
|
|
388
534
|
recordConsent(locale: ConsentLocale): Promise<RecordConsentResult>;
|
|
535
|
+
/**
|
|
536
|
+
* Send the person's identity method (profile v2 step 3) when the view offers
|
|
537
|
+
* a choice (`methodChoice.available`). Returns the updated hosted view —
|
|
538
|
+
* continue from it. 409 `method_locked` when the method can no longer
|
|
539
|
+
* change (a check of the chosen method was already submitted).
|
|
540
|
+
*/
|
|
541
|
+
selectMethod(method: IdentityMethod): Promise<HostedSessionView>;
|
|
389
542
|
/**
|
|
390
543
|
* Presign a Spaces PUT for a new pending object. `contentType` is stripped of
|
|
391
544
|
* MIME parameters defensively (the allowlist holds bare types; the engine's
|
|
@@ -446,4 +599,4 @@ declare class HostedClient {
|
|
|
446
599
|
private api;
|
|
447
600
|
}
|
|
448
601
|
|
|
449
|
-
export { type
|
|
602
|
+
export { type AcceptedDocumentType as A, type MediaCheckType as B, type CheckReason as C, type DecisionReason as D, type MediaKind as E, type FaceMatchReason as F, type PhoneOtpInput as G, type HostedBranding as H, type IdentityMethod as I, type PopulationInput as J, type PresignRequest as K, type LivenessReason as L, type MatchReason as M, type PresignUploadResult as N, type SessionStepView as O, type PhoneOtpReason as P, type SubmitCheckBody as Q, type RecordConsentResult as R, type SessionFailureReason as S, type SubmitOptions as T, type UploadMedia as U, stripMimeParams as V, type CheckState as a, type CheckType as b, type DocReason as c, type HostedBrandingColorKey as d, type HostedOtpView as e, type HostedSessionView as f, type HostedStepView as g, type HostedVoiceLocale as h, type HostedVoiceView as i, type MethodChoiceView as j, type PolicyFailureReason as k, type PopulationReason as l, type SessionOutcome as m, type SessionStatus as n, HostedClient as o, type AttestationInput as p, type SessionView as q, type CheckInputByType as r, type CompleteMediaResult as s, type ConsentLocale as t, type DocOcrInput as u, type DocSide as v, type FaceMatchInput as w, type HostedClientOptions as x, type LivenessCaptureInput as y, type LivenessInput as z };
|