@sekyro/node 0.1.0-rc.2 → 0.1.0-rc.4
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 +40 -0
- package/LICENSE +25 -16
- package/README.md +69 -5
- package/dist/index.cjs +1 -308
- package/dist/index.d.cts +864 -13
- package/dist/index.d.ts +864 -13
- package/dist/index.js +1 -272
- package/package.json +7 -10
- package/sbom.cdx.json +153 -0
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Wire-level enums and shapes of the Sekyro partner API (`/api/v2`). These
|
|
2
|
+
* Wire-level enums and shapes of the Sekyro AI partner API (`/api/v2`). These
|
|
3
3
|
* mirror the backend's frozen partner contract; they are plain string unions so
|
|
4
4
|
* a newer API value never throws at runtime — it simply widens the type in the
|
|
5
5
|
* next SDK release.
|
|
@@ -14,10 +14,81 @@ type SessionStatus = "created" | "in_progress" | "review_pending" | "completed"
|
|
|
14
14
|
* @public
|
|
15
15
|
*/
|
|
16
16
|
type SessionOutcome = "approved" | "review" | "rejected";
|
|
17
|
+
/**
|
|
18
|
+
* How the person proves their identity on an optional-document app: type the
|
|
19
|
+
* national number, or photograph the document (`document_chip`: native NFC clients only).
|
|
20
|
+
* `national_eid`: the country's national eID (e.g. Houwiyeti for Mauritania),
|
|
21
|
+
* offered when the app's profile turns the national eID on.
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
type IdentityMethod = "national_number" | "document_photo" | "document_chip" | "national_eid";
|
|
17
25
|
/** The verification checks a flow may contain. @public */
|
|
18
|
-
type CheckType = "liveness" | "face_match" | "doc_ocr" | "population_registry" | "phone_otp";
|
|
26
|
+
type CheckType = "liveness" | "face_match" | "doc_ocr" | "population_registry" | "phone_otp" | "nfc_chip" | "email_otp" | "portrait_icao" | "eid_auth";
|
|
19
27
|
/** Per-check execution state within a session. @public */
|
|
20
28
|
type CheckState = "pending" | "input_required" | "processing" | "passed" | "failed" | "review" | "skipped";
|
|
29
|
+
/**
|
|
30
|
+
* Why a session was stopped by a profile-v2 policy gate rather than by a
|
|
31
|
+
* failed check:
|
|
32
|
+
* - `location_not_allowed` — the end user's IP location is not allowed for the app;
|
|
33
|
+
* - `anonymizer_blocked` — an anonymizer (VPN / proxy / Tor) was detected while the app blocks them;
|
|
34
|
+
* - `capture_tampered` — the device reported a tampered capture (virtual
|
|
35
|
+
* camera, hooking, emulator…) while the app's `device.tamper` is `block`;
|
|
36
|
+
* - `device_velocity_exceeded` — the device already verified more different
|
|
37
|
+
* people than `device.velocity` allows, with `action: 'block'`;
|
|
38
|
+
* - `device_cooldown` — the device had too many rejected sessions
|
|
39
|
+
* (`cooldown` tiers); {@link Session.retryAfter} says when it may try again.
|
|
40
|
+
* Not billed.
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
type SessionFailureReason = "location_not_allowed" | "anonymizer_blocked" | "capture_tampered" | "device_velocity_exceeded" | "device_cooldown";
|
|
44
|
+
/**
|
|
45
|
+
* Profile-v2 decision-mode reasons a check's {@link SessionStep.reason} may
|
|
46
|
+
* carry when the app decides automatically (`decisions.mode: 'automatic'`)
|
|
47
|
+
* instead of routing to human review. All are retryable within the step's
|
|
48
|
+
* attempts: `liveness_inconclusive` (liveness middle band),
|
|
49
|
+
* `match_inconclusive` (face match middle band on every reference),
|
|
50
|
+
* `document_authenticity` (doc_ocr authenticity warning — retake the original)
|
|
51
|
+
* and `mobile_required` (liveness captured on a desktop — continue on a phone;
|
|
52
|
+
* not billed). Every inconclusive attempt is billed.
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
type DecisionReason = "liveness_inconclusive" | "match_inconclusive" | "document_authenticity" | "mobile_required";
|
|
56
|
+
/**
|
|
57
|
+
* Profile-v2 eligibility reasons a check's {@link SessionStep.reason} may carry:
|
|
58
|
+
* `document_type_not_accepted` (doc_ocr; the person may pick another
|
|
59
|
+
* document), `nationality_not_allowed` and `underage` (doc_ocr or
|
|
60
|
+
* population_registry; terminal), and `age_unverifiable` (the app sets a
|
|
61
|
+
* minimum age but the birth date could not be read — doc_ocr: retryable with
|
|
62
|
+
* a clearer photo; population_registry: terminal).
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
type EligibilityReason = "document_type_not_accepted" | "nationality_not_allowed" | "underage" | "age_unverifiable";
|
|
66
|
+
/**
|
|
67
|
+
* Why a profile-v2 policy ended a session: a session-level
|
|
68
|
+
* {@link SessionFailureReason} (location / anonymizer gate), or the failed
|
|
69
|
+
* check's policy reason — an {@link EligibilityReason} or
|
|
70
|
+
* `document_nni_mismatch` (the document belongs to someone other than the
|
|
71
|
+
* NNI the session was created for).
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
74
|
+
type PolicyFailureReason = SessionFailureReason | EligibilityReason | "document_nni_mismatch";
|
|
75
|
+
/**
|
|
76
|
+
* Profile-v2 matching reasons a check's {@link SessionStep.reason} may carry
|
|
77
|
+
* when the data you sent in {@link CreateSessionInput.subject} differs from the
|
|
78
|
+
* reference identity: `name_mismatch`, `birth_date_mismatch`,
|
|
79
|
+
* `nationality_mismatch` (doc_ocr or population_registry) and
|
|
80
|
+
* `document_number_mismatch` (doc_ocr). All are final.
|
|
81
|
+
* @public
|
|
82
|
+
*/
|
|
83
|
+
type MatchReason = "name_mismatch" | "birth_date_mismatch" | "nationality_mismatch" | "document_number_mismatch";
|
|
84
|
+
/**
|
|
85
|
+
* Face-match probe-binding reason a `face_match` {@link SessionStep.reason} may
|
|
86
|
+
* carry (SDK spec §4.10): `probe_not_from_liveness` — the face image isn't the
|
|
87
|
+
* person in the liveness clip. Retryable within the step's attempts (the
|
|
88
|
+
* person re-takes the liveness capture); billed.
|
|
89
|
+
* @public
|
|
90
|
+
*/
|
|
91
|
+
type ProbeBindingReason = "probe_not_from_liveness";
|
|
21
92
|
/** Hosted-flow languages. @public */
|
|
22
93
|
type Locale = "ar" | "fr" | "en";
|
|
23
94
|
/** The environment a webhook was emitted from. @public */
|
|
@@ -34,9 +105,62 @@ interface SessionStep {
|
|
|
34
105
|
reason?: string;
|
|
35
106
|
/** Calibrated model score in [0, 1] for scored checks (liveness, face match). */
|
|
36
107
|
calibratedScore?: number;
|
|
108
|
+
/** The check failed after using all of its attempts. */
|
|
109
|
+
attemptsExhausted?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* `portrait_icao` only (ID mode): the per-criterion ICAO report of the last
|
|
112
|
+
* attempt — numbers and enums only, never the image.
|
|
113
|
+
*/
|
|
114
|
+
report?: PortraitReportEntry[];
|
|
115
|
+
/**
|
|
116
|
+
* `face_match` only: the probe-quality measurement of the selfie that was
|
|
117
|
+
* matched (occlusion, head pose, eye openness, sharpness). Recorded for
|
|
118
|
+
* information only: it never changes the step's state or reason.
|
|
119
|
+
*/
|
|
120
|
+
probeQuality?: ProbeQuality;
|
|
37
121
|
}
|
|
38
122
|
/**
|
|
39
|
-
*
|
|
123
|
+
* The probe-quality measurement on a `face_match` step. Numbers only; `null`
|
|
124
|
+
* means the value could not be measured. Informational: no threshold applies.
|
|
125
|
+
* @public
|
|
126
|
+
*/
|
|
127
|
+
interface ProbeQuality {
|
|
128
|
+
/** Share of the inner face that is covered (a hand, a mask, a phone), in [0, 1]. */
|
|
129
|
+
occlusion: number | null;
|
|
130
|
+
/** Head pose in degrees. */
|
|
131
|
+
pose: {
|
|
132
|
+
yaw: number | null;
|
|
133
|
+
pitch: number | null;
|
|
134
|
+
roll: number | null;
|
|
135
|
+
};
|
|
136
|
+
/** Openness of the less-open eye, in [0, 1]. */
|
|
137
|
+
eyesOpen: number | null;
|
|
138
|
+
/** Sharpness of the face region (higher is sharper; no fixed scale). */
|
|
139
|
+
sharpness: number | null;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* One row of the `portrait_icao` report (ID mode): the server's measurement of
|
|
143
|
+
* one ICAO criterion and the threshold that applied.
|
|
144
|
+
* @public
|
|
145
|
+
*/
|
|
146
|
+
interface PortraitReportEntry {
|
|
147
|
+
criterion: "pose" | "eyes" | "mouth" | "occlusion" | "glare" | "lighting" | "background" | "resolution";
|
|
148
|
+
value: number | null;
|
|
149
|
+
threshold: number | string | null;
|
|
150
|
+
pass: boolean;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* A short-lived link to a session's ICAO portrait (480×640 JPEG), from
|
|
154
|
+
* {@link SessionsApi.portrait}. Download it before `expiresAt`; never store the URL.
|
|
155
|
+
* @public
|
|
156
|
+
*/
|
|
157
|
+
interface SessionPortraitLink {
|
|
158
|
+
url: string;
|
|
159
|
+
/** ISO 8601 (about 5 minutes after the call). */
|
|
160
|
+
expiresAt: string;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Out-of-band delivery of the session's hosted link, sent by Sekyro AI after the
|
|
40
164
|
* session is created. Best effort: a failed send never fails the create.
|
|
41
165
|
* @public
|
|
42
166
|
*/
|
|
@@ -46,6 +170,225 @@ interface DeliverOptions {
|
|
|
46
170
|
/** E.164 phone number of the recipient, e.g. `+22236123456`. */
|
|
47
171
|
to: string;
|
|
48
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* What you already know about the person (`subject` at session create). A
|
|
175
|
+
* closed object: unknown keys are refused (`400`). Every field is optional;
|
|
176
|
+
* `null` or blank counts as absent. Identity fields are compared with the
|
|
177
|
+
* person's document or registry record when the app's profile turns name
|
|
178
|
+
* matching on; `nni` binds the session to that identity.
|
|
179
|
+
* @public
|
|
180
|
+
*/
|
|
181
|
+
interface SessionSubjectInput {
|
|
182
|
+
/** 10-digit Mauritanian NNI, as a string. */
|
|
183
|
+
nni?: string;
|
|
184
|
+
/** E.164 phone number, e.g. `+22236123456`. */
|
|
185
|
+
phone?: string;
|
|
186
|
+
fullName?: string;
|
|
187
|
+
/** ISO 8601 date (`YYYY-MM-DD`). */
|
|
188
|
+
birthDate?: string;
|
|
189
|
+
/** ISO 3166-1 alpha-2, e.g. `MR`. */
|
|
190
|
+
nationality?: string;
|
|
191
|
+
/** Identity-document number (1–40 characters). */
|
|
192
|
+
documentNumber?: string;
|
|
193
|
+
/**
|
|
194
|
+
* Your own id for this person (1–128 characters). Echoed on the session and
|
|
195
|
+
* its webhooks, and searchable on the subjects API.
|
|
196
|
+
*/
|
|
197
|
+
externalRef?: string;
|
|
198
|
+
/**
|
|
199
|
+
* Returning user (profile v2 step 5): verify this already-verified subject
|
|
200
|
+
* again with liveness + face match only, against the reference photo stored
|
|
201
|
+
* on the subject. Name exactly one target. Errors at create:
|
|
202
|
+
* `404 subject_not_found`, `409 subject_revoked`, `409 subject_no_reference`,
|
|
203
|
+
* `409 subject_stale` (older than the app's `reuse.maxAgeMonths`) and
|
|
204
|
+
* `400` / `409 reuse_target_ambiguous`.
|
|
205
|
+
*/
|
|
206
|
+
reuse?: SubjectReuseTarget;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Which subject a returning-user session re-verifies — exactly ONE of the
|
|
210
|
+
* subject id, your own `externalRef` (must match a single subject) or the NNI.
|
|
211
|
+
* @public
|
|
212
|
+
*/
|
|
213
|
+
type SubjectReuseTarget = {
|
|
214
|
+
subjectId: string;
|
|
215
|
+
externalRef?: never;
|
|
216
|
+
nni?: never;
|
|
217
|
+
} | {
|
|
218
|
+
externalRef: string;
|
|
219
|
+
subjectId?: never;
|
|
220
|
+
nni?: never;
|
|
221
|
+
} | {
|
|
222
|
+
nni: string;
|
|
223
|
+
subjectId?: never;
|
|
224
|
+
externalRef?: never;
|
|
225
|
+
};
|
|
226
|
+
/** A document type the backend reads (kyc-docs-v2 vocabulary; `carte_identite` = identity card of any issuer). @public */
|
|
227
|
+
type ProfileDocumentType = "carte_identite" | "passport" | "residence_permit";
|
|
228
|
+
/**
|
|
229
|
+
* The settings of an app's eKYC profile that a session may override — a
|
|
230
|
+
* mirror of the backend profile (schema version 2) without `schemaVersion`,
|
|
231
|
+
* `link` (use {@link CreateSessionInput.ttlMinutes}) and `overrides` (the
|
|
232
|
+
* app's own permission). Country codes are ISO 3166-1 alpha-2 (`MR`).
|
|
233
|
+
* Used through {@link EkycProfileOverride}.
|
|
234
|
+
* @public
|
|
235
|
+
*/
|
|
236
|
+
interface EkycProfileSettings {
|
|
237
|
+
/**
|
|
238
|
+
* Phone OTP step: off, or on with where the number comes from. The person
|
|
239
|
+
* chooses WhatsApp or SMS from the channels left after the org's settings
|
|
240
|
+
* and `channels`.
|
|
241
|
+
*/
|
|
242
|
+
phone: "off" | {
|
|
243
|
+
/** Where the number comes from: your `subject.phone` / `input.phone`, the person, or either. */
|
|
244
|
+
numberSource: "prefilled" | "user_entered" | "prefilled_or_entered";
|
|
245
|
+
/**
|
|
246
|
+
* Narrow the channels the person may choose from (absent = every
|
|
247
|
+
* channel the org has on). A channel the org turned off answers
|
|
248
|
+
* `400 phone_channels_disabled_for_org`; an empty or duplicated list
|
|
249
|
+
* `400 phone_channels_invalid`.
|
|
250
|
+
*/
|
|
251
|
+
channels?: Array<"whatsapp" | "sms">;
|
|
252
|
+
/**
|
|
253
|
+
* Which countries codes may be sent to: the profile's allowed
|
|
254
|
+
* `locations` (default), `any`, or a list of ISO 3166-1 alpha-2 codes.
|
|
255
|
+
* Invalid values answer `400 phone_destinations_invalid`.
|
|
256
|
+
*/
|
|
257
|
+
destinations?: "locations" | "any" | {
|
|
258
|
+
countries: string[];
|
|
259
|
+
};
|
|
260
|
+
};
|
|
261
|
+
/** Email OTP step (absent = off): a one-time code sent to the person's email address. */
|
|
262
|
+
email?: "off" | ProfileEmail;
|
|
263
|
+
document: {
|
|
264
|
+
mode: "off" | "optional" | "mandatory";
|
|
265
|
+
/** Accepted document types: `'*'` (all) or a list, optionally per issuing country. */
|
|
266
|
+
accepted: {
|
|
267
|
+
default: "*" | ProfileDocumentType[];
|
|
268
|
+
byIssuer?: Record<string, ProfileDocumentType[]>;
|
|
269
|
+
};
|
|
270
|
+
/** Expired documents: refused, allowed, or allowed for `graceDays` (1–365) after expiry. */
|
|
271
|
+
expired: "reject" | "allow" | {
|
|
272
|
+
graceDays: number;
|
|
273
|
+
};
|
|
274
|
+
authenticity: "standard" | "off";
|
|
275
|
+
};
|
|
276
|
+
registry: {
|
|
277
|
+
enabled: boolean;
|
|
278
|
+
sources: Array<"MR.anrpts">;
|
|
279
|
+
/** Which identity is the reference for face match and your data. */
|
|
280
|
+
reference?: "document" | "registry";
|
|
281
|
+
/** What happens when the registry has no record for the NNI. */
|
|
282
|
+
onNoRecord?: "fail" | "use_document";
|
|
283
|
+
};
|
|
284
|
+
liveness: {
|
|
285
|
+
enabled: boolean;
|
|
286
|
+
};
|
|
287
|
+
faceMatch: {
|
|
288
|
+
enabled: boolean;
|
|
289
|
+
};
|
|
290
|
+
/** Comparison of your `subject` data with the reference identity. */
|
|
291
|
+
nameMatch?: {
|
|
292
|
+
level: "strict" | "standard" | "lenient";
|
|
293
|
+
onMismatch: "fail" | "signal";
|
|
294
|
+
};
|
|
295
|
+
/** Per-step attempt limits. */
|
|
296
|
+
attempts?: {
|
|
297
|
+
phone: number;
|
|
298
|
+
/** Email OTP attempts (1–5); absent = the platform default. */
|
|
299
|
+
email?: number;
|
|
300
|
+
document: number;
|
|
301
|
+
registry: number;
|
|
302
|
+
liveness: number;
|
|
303
|
+
faceMatch: number;
|
|
304
|
+
};
|
|
305
|
+
/** Where the end user may be (IP location). */
|
|
306
|
+
locations: {
|
|
307
|
+
mode: "all";
|
|
308
|
+
} | {
|
|
309
|
+
mode: "only" | "all_except";
|
|
310
|
+
countries: string[];
|
|
311
|
+
};
|
|
312
|
+
anonymizers: "allow" | "block";
|
|
313
|
+
/** Accepted nationalities: `'*'` or a list, optionally per IP location. */
|
|
314
|
+
nationalities: {
|
|
315
|
+
default: "*" | string[];
|
|
316
|
+
byLocation?: Record<string, "*" | string[]>;
|
|
317
|
+
};
|
|
318
|
+
/** Minimum age in years (1–120), or `null` for none. */
|
|
319
|
+
minAge: number | null;
|
|
320
|
+
/** Automatic decisions instead of human review (`retryConfidentRejects` lets confident rejects retry). */
|
|
321
|
+
decisions?: {
|
|
322
|
+
mode: "automatic" | "review";
|
|
323
|
+
retryConfidentRejects: boolean;
|
|
324
|
+
};
|
|
325
|
+
/** Device cooldown after repeated rejections (at most 5 tiers; `[]` = off). */
|
|
326
|
+
cooldown?: {
|
|
327
|
+
tiers: Array<{
|
|
328
|
+
rejectedSessions: number;
|
|
329
|
+
windowHours: number;
|
|
330
|
+
waitMinutes: number;
|
|
331
|
+
}>;
|
|
332
|
+
};
|
|
333
|
+
/** Device tamper and velocity actions. */
|
|
334
|
+
device?: {
|
|
335
|
+
tamper: "block" | "signal";
|
|
336
|
+
velocity: {
|
|
337
|
+
maxIdentitiesPerDevice: number;
|
|
338
|
+
windowDays: number;
|
|
339
|
+
action: "signal" | "block";
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* The live photo. `purpose: "id_document"` also takes an ICAO 480×640 portrait you
|
|
344
|
+
* download for printing on an ID document. `icao` makes a verification selfie meet
|
|
345
|
+
* the ICAO photo rules: `"reject"` asks the person to retake a non-compliant photo
|
|
346
|
+
* (after the last attempt the session fails, or goes to review in review mode);
|
|
347
|
+
* `"alert"` lets the person continue and adds the risk signal `icao_non_compliant`.
|
|
348
|
+
* Both need liveness on; `"alert"` with `"id_document"` answers
|
|
349
|
+
* `400 portrait_icao_policy_id_document`. Absent = a verification selfie, no ICAO check.
|
|
350
|
+
*/
|
|
351
|
+
portrait?: {
|
|
352
|
+
purpose?: "verification" | "id_document";
|
|
353
|
+
icao?: {
|
|
354
|
+
policy: "alert" | "reject";
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* The email OTP step of a profile. When the email check is not available on
|
|
360
|
+
* the platform yet, saving it answers `400 email_otp_not_available`.
|
|
361
|
+
* @public
|
|
362
|
+
*/
|
|
363
|
+
interface ProfileEmail {
|
|
364
|
+
/** Where the address comes from: your `input.email`, the person, or either. */
|
|
365
|
+
emailSource: "prefilled" | "user_entered" | "prefilled_or_entered";
|
|
366
|
+
/** Disposable (temporary) addresses: allowed, flagged as a risk signal (default), or refused. */
|
|
367
|
+
disposable?: "allow" | "signal" | "block";
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* `T` with every property optional, recursively. Arrays are replaced as a
|
|
371
|
+
* whole, never merged element by element.
|
|
372
|
+
* @public
|
|
373
|
+
*/
|
|
374
|
+
type DeepPartial<T> = T extends readonly unknown[] ? T : T extends object ? {
|
|
375
|
+
[K in keyof T]?: DeepPartial<T[K]>;
|
|
376
|
+
} : T;
|
|
377
|
+
/**
|
|
378
|
+
* A per-session override of the app's eKYC profile
|
|
379
|
+
* ({@link CreateSessionInput.profile}): any subset of
|
|
380
|
+
* {@link EkycProfileSettings}, at any depth (every field optional). Objects
|
|
381
|
+
* are merged into the app's profile; arrays and scalar values replace it.
|
|
382
|
+
*
|
|
383
|
+
* The API validates the merged result like a profile save. Unknown keys are
|
|
384
|
+
* rejected server-side (`400`, also for keys this SDK version does not type).
|
|
385
|
+
* An override may only make the profile stricter unless the app allows
|
|
386
|
+
* widening (`overrides.allowWidening`); otherwise a widening change answers
|
|
387
|
+
* `403 override_widens_profile`. The changed paths are reported on
|
|
388
|
+
* {@link Session.overriddenFields}.
|
|
389
|
+
* @public
|
|
390
|
+
*/
|
|
391
|
+
type EkycProfileOverride = DeepPartial<EkycProfileSettings>;
|
|
49
392
|
/**
|
|
50
393
|
* Input of {@link SessionsApi.create}. There is no recipe field: the API key
|
|
51
394
|
* belongs to an app, and that app's eKYC profile decides the steps.
|
|
@@ -54,6 +397,11 @@ interface DeliverOptions {
|
|
|
54
397
|
interface CreateSessionInput {
|
|
55
398
|
/** Pins the hosted-flow language. Omit to let the person's device decide. */
|
|
56
399
|
locale?: Locale;
|
|
400
|
+
/**
|
|
401
|
+
* Pre-choose the identity method when the app's document step is optional
|
|
402
|
+
* (the hosted method screen is then skipped). Ignored for other apps.
|
|
403
|
+
*/
|
|
404
|
+
identityMethod?: IdentityMethod;
|
|
57
405
|
/**
|
|
58
406
|
* Session lifetime in minutes. Must fall within the app profile's link
|
|
59
407
|
* range, otherwise the API answers `400 ttl_out_of_policy`. Omit to use the
|
|
@@ -62,6 +410,19 @@ interface CreateSessionInput {
|
|
|
62
410
|
ttlMinutes?: number;
|
|
63
411
|
/** Also send the hosted link to the person by SMS or WhatsApp. */
|
|
64
412
|
deliver?: DeliverOptions;
|
|
413
|
+
/**
|
|
414
|
+
* What you already know about the person (preferred over `input` for
|
|
415
|
+
* identity data). A key sent in both `subject` and `input` with different
|
|
416
|
+
* values answers `400 subject_input_conflict`.
|
|
417
|
+
*/
|
|
418
|
+
subject?: SessionSubjectInput;
|
|
419
|
+
/**
|
|
420
|
+
* Override the app's eKYC profile for this session only (profile v2): a
|
|
421
|
+
* deep-partial {@link EkycProfileOverride}. Unknown keys are rejected
|
|
422
|
+
* server-side; a change that makes the profile less strict answers
|
|
423
|
+
* `403 override_widens_profile` unless the app allows widening.
|
|
424
|
+
*/
|
|
425
|
+
profile?: EkycProfileOverride;
|
|
65
426
|
/**
|
|
66
427
|
* Step inputs consumed server-side. The known anchors are typed (all
|
|
67
428
|
* strings — an `nni` is a 10-digit string, never a number); further keys
|
|
@@ -73,11 +434,59 @@ interface CreateSessionInput {
|
|
|
73
434
|
nni?: string;
|
|
74
435
|
/** E.164 phone number, e.g. `+22236123456`. */
|
|
75
436
|
phone?: string;
|
|
437
|
+
/** Email address for the email OTP step (`emailSource` `prefilled` / `prefilled_or_entered`). */
|
|
438
|
+
email?: string;
|
|
76
439
|
fullName?: string;
|
|
77
440
|
/** ISO 8601 date (`YYYY-MM-DD`). */
|
|
78
441
|
birthDate?: string;
|
|
442
|
+
/**
|
|
443
|
+
* Partner-supplied identity data (`supplied` mode only): the person's
|
|
444
|
+
* biographic record and photo as YOUR system holds them — for example
|
|
445
|
+
* from your own ANRPTS answer, converted with {@link registryFromAnrpts}.
|
|
446
|
+
* Sekyro AI stores it as data you vouch for, makes no registry call, and
|
|
447
|
+
* never receives or stores any registry key. `nni` must be set too and
|
|
448
|
+
* equal `suppliedIdentity.nni`. Refused unless Sekyro AI enabled the mode
|
|
449
|
+
* for the environment (`403 supplied_identity_not_entitled`); required in
|
|
450
|
+
* that mode when the app verifies identity (`422 supplied_identity_required`).
|
|
451
|
+
*/
|
|
452
|
+
suppliedIdentity?: SuppliedIdentityRecord;
|
|
79
453
|
} & Record<string, unknown>;
|
|
80
454
|
}
|
|
455
|
+
/**
|
|
456
|
+
* Partner-supplied identity data (`sessions.create` `input.suppliedIdentity`):
|
|
457
|
+
* the person's biographic record and photo, as your organization holds them.
|
|
458
|
+
* Sekyro AI treats it as identity data supplied by your organization, not as
|
|
459
|
+
* a registry lookup. Refusals at create: `400 supplied_identity_nni_mismatch`,
|
|
460
|
+
* `422 supplied_identity_stale` (`fetchedAt` older than 24 h),
|
|
461
|
+
* `422 supplied_identity_photo_unusable` (not a JPEG/PNG of at most 1 MB within the
|
|
462
|
+
* size bounds). In a `supplied` environment whose app verifies identity, a create
|
|
463
|
+
* without `input.suppliedIdentity` is `422 supplied_identity_required`. The face on
|
|
464
|
+
* the photo is checked after consent, at the face match (`no_face_reference`).
|
|
465
|
+
* @public
|
|
466
|
+
*/
|
|
467
|
+
interface SuppliedIdentityRecord {
|
|
468
|
+
/** 10-digit NNI, as a string. */
|
|
469
|
+
nni: string;
|
|
470
|
+
firstNameFr: string;
|
|
471
|
+
lastNameFr: string;
|
|
472
|
+
fatherNameFr?: string;
|
|
473
|
+
firstNameAr: string;
|
|
474
|
+
lastNameAr: string;
|
|
475
|
+
fatherNameAr?: string;
|
|
476
|
+
/** ISO 8601 date (`YYYY-MM-DD`). */
|
|
477
|
+
birthDate: string;
|
|
478
|
+
birthPlaceFr?: string;
|
|
479
|
+
birthPlaceAr?: string;
|
|
480
|
+
sex?: "M" | "F";
|
|
481
|
+
/** Nationality text as your source holds it (e.g. `Mauritanienne`). */
|
|
482
|
+
nationalities?: string[];
|
|
483
|
+
/** Base64 JPEG or PNG (at most 1 MB decoded). */
|
|
484
|
+
photo: string;
|
|
485
|
+
/** ISO 8601 date (`YYYY-MM-DD`) of the registry photo. */
|
|
486
|
+
photoDate?: string;
|
|
487
|
+
/** ISO 8601 date-time at which your system obtained this record. */
|
|
488
|
+
fetchedAt: string;
|
|
489
|
+
}
|
|
81
490
|
/** Per-request options. @public */
|
|
82
491
|
interface RequestOptions {
|
|
83
492
|
/**
|
|
@@ -102,6 +511,100 @@ interface CreatedSession {
|
|
|
102
511
|
/** ISO 8601 — when the session (and its link) stop accepting input. */
|
|
103
512
|
expiresAt: string;
|
|
104
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* The end user's network at the start of the session (IP-derived, approximate).
|
|
516
|
+
* @public
|
|
517
|
+
*/
|
|
518
|
+
interface SessionNetworkEvent {
|
|
519
|
+
/** ISO 8601. */
|
|
520
|
+
at: string;
|
|
521
|
+
/** ISO 3166-1 alpha-2. */
|
|
522
|
+
country?: string;
|
|
523
|
+
region?: string;
|
|
524
|
+
city?: string;
|
|
525
|
+
asn?: number;
|
|
526
|
+
operator?: string;
|
|
527
|
+
connectionType: "mobile" | "hosting" | "unknown";
|
|
528
|
+
anonymizer: {
|
|
529
|
+
vpn: boolean;
|
|
530
|
+
tor: boolean;
|
|
531
|
+
hosting: boolean;
|
|
532
|
+
};
|
|
533
|
+
/** IANA time zone reported by the end user's device. */
|
|
534
|
+
browserTimezone?: string;
|
|
535
|
+
/** True when the device time zone does not belong to the IP country. */
|
|
536
|
+
timezoneMismatch?: boolean;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* End-user network context; absent when not recorded.
|
|
540
|
+
* @public
|
|
541
|
+
*/
|
|
542
|
+
interface SessionNetwork {
|
|
543
|
+
/** The start event's IP address. Never sent in webhooks. */
|
|
544
|
+
ip?: string;
|
|
545
|
+
/** True when a later call came from a different country than the start. */
|
|
546
|
+
countryChanged: boolean;
|
|
547
|
+
start: SessionNetworkEvent;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Device signals detected on the end user's device.
|
|
551
|
+
* @public
|
|
552
|
+
*/
|
|
553
|
+
type SessionDeviceSignal = "webdriver" | "headless" | "emulator" | "rooted" | "jailbroken" | "debugger" | "hooking" | "virtual_camera";
|
|
554
|
+
/**
|
|
555
|
+
* End-user device summary; absent when not recorded.
|
|
556
|
+
* @public
|
|
557
|
+
*/
|
|
558
|
+
interface SessionDevice {
|
|
559
|
+
platform: "web" | "android" | "ios";
|
|
560
|
+
sdk?: string;
|
|
561
|
+
os?: string;
|
|
562
|
+
osVersion?: string;
|
|
563
|
+
browser?: string;
|
|
564
|
+
browserVersion?: string;
|
|
565
|
+
deviceClass: "mobile" | "tablet" | "desktop" | "unknown";
|
|
566
|
+
model?: string;
|
|
567
|
+
/** Device ids hashed with a key unique to your organization. */
|
|
568
|
+
deviceKeys: string[];
|
|
569
|
+
signals: SessionDeviceSignal[];
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* The national eID that verified the session's identity (the `eid_auth` check
|
|
573
|
+
* passed): the provider id (`{country}.{provider}`, e.g. `MR.houwiyeti`), its
|
|
574
|
+
* ISO 3166-1 alpha-2 country and when. A passed eID ends the session.
|
|
575
|
+
* @public
|
|
576
|
+
*/
|
|
577
|
+
interface EidResult {
|
|
578
|
+
provider: string;
|
|
579
|
+
country: string;
|
|
580
|
+
verifiedAt: string;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* The full phone number and email address the person verified in a session
|
|
584
|
+
* (an OTP check that passed) — sent ONLY to you (the session view and your
|
|
585
|
+
* webhooks), never to the person's browser, and removed when the session's
|
|
586
|
+
* personal data is purged. You are the controller of these values. A
|
|
587
|
+
* prefilled value that was not verified is never listed.
|
|
588
|
+
* @public
|
|
589
|
+
*/
|
|
590
|
+
interface VerifiedContacts {
|
|
591
|
+
/** From a passed `phone_otp` check. */
|
|
592
|
+
phone?: {
|
|
593
|
+
/** E.164, e.g. `+22236123456`. */
|
|
594
|
+
number: string;
|
|
595
|
+
/** The channel the code was delivered on. */
|
|
596
|
+
channel: "whatsapp" | "sms";
|
|
597
|
+
/** ISO 8601. */
|
|
598
|
+
verifiedAt: string;
|
|
599
|
+
};
|
|
600
|
+
/** From a passed `email_otp` check. */
|
|
601
|
+
email?: {
|
|
602
|
+
/** The verified address (domain lowercased). */
|
|
603
|
+
address: string;
|
|
604
|
+
/** ISO 8601. */
|
|
605
|
+
verifiedAt: string;
|
|
606
|
+
};
|
|
607
|
+
}
|
|
105
608
|
/**
|
|
106
609
|
* The whitelisted session view returned by {@link SessionsApi.get}. Never
|
|
107
610
|
* contains the client token.
|
|
@@ -116,6 +619,55 @@ interface Session {
|
|
|
116
619
|
expiresAt: string;
|
|
117
620
|
/** ISO 8601. */
|
|
118
621
|
createdAt: string;
|
|
622
|
+
/** End-user network context; absent when not recorded. */
|
|
623
|
+
network?: SessionNetwork;
|
|
624
|
+
/** End-user device; absent when not recorded. */
|
|
625
|
+
device?: SessionDevice;
|
|
626
|
+
/**
|
|
627
|
+
* Set when a profile-v2 policy ended the session: a session-level gate
|
|
628
|
+
* (location, anonymizer) or a failed check's policy reason (eligibility,
|
|
629
|
+
* `document_nni_mismatch`). Absent otherwise.
|
|
630
|
+
*/
|
|
631
|
+
failureReason?: PolicyFailureReason;
|
|
632
|
+
/** The subject (person) this session is linked to, once an identifier is known. */
|
|
633
|
+
subjectId?: string;
|
|
634
|
+
/** Your own id for the person, from `subject.externalRef` at create. */
|
|
635
|
+
externalRef?: string;
|
|
636
|
+
/**
|
|
637
|
+
* `true` when an identifier this session read already belongs to a
|
|
638
|
+
* different subject than `subjectId` — the subjects are never merged.
|
|
639
|
+
*/
|
|
640
|
+
subjectConflict?: boolean;
|
|
641
|
+
/**
|
|
642
|
+
* ISO 8601 — when the end user's device may verify again; set with
|
|
643
|
+
* `failureReason: 'device_cooldown'`.
|
|
644
|
+
*/
|
|
645
|
+
retryAfter?: string;
|
|
646
|
+
/** Dotted profile paths this session's {@link CreateSessionInput.profile} changed (e.g. `decisions.mode`). */
|
|
647
|
+
overriddenFields?: string[];
|
|
648
|
+
/**
|
|
649
|
+
* Risk signals recorded on the session (e.g. `device_velocity`,
|
|
650
|
+
* `timezone_mismatch`, `icao_non_compliant` when an `alert` ICAO policy flagged
|
|
651
|
+
* the selfie) — signals only, never a verdict by themselves.
|
|
652
|
+
*/
|
|
653
|
+
riskSignals?: string[];
|
|
654
|
+
/**
|
|
655
|
+
* `true` for a returning-user session (created with
|
|
656
|
+
* {@link SessionSubjectInput.reuse}): liveness + face match against the
|
|
657
|
+
* subject's stored reference photo only.
|
|
658
|
+
*/
|
|
659
|
+
returning?: true;
|
|
660
|
+
/** On a returning-user session: the subject (person) it re-verifies. */
|
|
661
|
+
reuseOf?: string;
|
|
662
|
+
/** The contacts verified in this session (passed OTP checks only); absent when none. */
|
|
663
|
+
verifiedContacts?: VerifiedContacts;
|
|
664
|
+
/** The national eID that verified this session (the `eid_auth` check passed); absent otherwise. */
|
|
665
|
+
eid?: EidResult;
|
|
666
|
+
/**
|
|
667
|
+
* ID mode (`portrait.purpose = id_document`) only: the ICAO portrait can be
|
|
668
|
+
* fetched with {@link SessionsApi.portrait}.
|
|
669
|
+
*/
|
|
670
|
+
portraitAvailable?: boolean;
|
|
119
671
|
}
|
|
120
672
|
/**
|
|
121
673
|
* `data` of the terminal / review-pending session lifecycle events
|
|
@@ -133,6 +685,169 @@ interface SessionLifecycleEventData {
|
|
|
133
685
|
steps?: Array<Pick<SessionStep, "checkType" | "state">>;
|
|
134
686
|
/** Machine-readable reason, when the transition has one. */
|
|
135
687
|
reason?: string;
|
|
688
|
+
/** End-user network context (no IP); on `completed` / `failed` when recorded. */
|
|
689
|
+
network?: Omit<SessionNetwork, "ip">;
|
|
690
|
+
/** End-user device; on `completed` / `failed` when recorded. */
|
|
691
|
+
device?: SessionDevice;
|
|
692
|
+
/**
|
|
693
|
+
* On `session.failed`: set when a profile-v2 policy ended the session — a
|
|
694
|
+
* session-level gate (location, anonymizer) or a failed check's policy
|
|
695
|
+
* reason (eligibility, `document_nni_mismatch`).
|
|
696
|
+
*/
|
|
697
|
+
failureReason?: PolicyFailureReason;
|
|
698
|
+
/** The subject (person) the session is linked to, when known. */
|
|
699
|
+
subjectId?: string;
|
|
700
|
+
/** Your own id for the person, from `subject.externalRef` at create. */
|
|
701
|
+
externalRef?: string;
|
|
702
|
+
/**
|
|
703
|
+
* `true` when an identifier this session read already belongs to a
|
|
704
|
+
* different subject than `subjectId` — the subjects are never merged.
|
|
705
|
+
*/
|
|
706
|
+
subjectConflict?: boolean;
|
|
707
|
+
/** `true` on a returning-user session (liveness + face match against the stored photo). */
|
|
708
|
+
returning?: true;
|
|
709
|
+
/** On a returning-user session: the subject (person) it re-verifies. */
|
|
710
|
+
reuseOf?: string;
|
|
711
|
+
/**
|
|
712
|
+
* On `session.failed` with `failureReason: 'device_cooldown'`: ISO time
|
|
713
|
+
* from which the device may start a new verification.
|
|
714
|
+
*/
|
|
715
|
+
retryAfter?: string;
|
|
716
|
+
/** Dotted profile paths the session's create-time override changed. */
|
|
717
|
+
overriddenFields?: string[];
|
|
718
|
+
/** On `completed` / `failed`: the contacts verified in this session (passed OTP checks only). */
|
|
719
|
+
verifiedContacts?: VerifiedContacts;
|
|
720
|
+
/** On `completed` / `failed`: the national eID that verified this session, when one did. */
|
|
721
|
+
eid?: EidResult;
|
|
722
|
+
/**
|
|
723
|
+
* ID mode only, on `completed` / `failed`: the ICAO portrait can be fetched
|
|
724
|
+
* with {@link SessionsApi.portrait} (the event never carries the image).
|
|
725
|
+
*/
|
|
726
|
+
portraitAvailable?: boolean;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* `data` of `subject.updated` — either a session linked to the subject reached
|
|
730
|
+
* a terminal state (`sessionId`, session `status`, `outcome`), or the subject
|
|
731
|
+
* was revoked (`status: 'revoked'`, no session). Ids and enums only.
|
|
732
|
+
* @public
|
|
733
|
+
*/
|
|
734
|
+
interface SubjectUpdatedEventData {
|
|
735
|
+
subjectId: string;
|
|
736
|
+
/** The session that reached a terminal state; absent on a revocation. */
|
|
737
|
+
sessionId?: string;
|
|
738
|
+
status: SessionStatus | "revoked";
|
|
739
|
+
/** The session outcome; absent on a revocation. */
|
|
740
|
+
outcome?: SessionOutcome | null;
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* `data` of `subject.enrolled` — the subject's first reference photo was
|
|
744
|
+
* stored (it can now be used for returning-user sessions). Ids and enums only.
|
|
745
|
+
* @public
|
|
746
|
+
*/
|
|
747
|
+
interface SubjectEnrolledEventData {
|
|
748
|
+
subjectId: string;
|
|
749
|
+
/** The approved session the reference photo came from. */
|
|
750
|
+
sessionId: string;
|
|
751
|
+
assurance: SubjectAssurance;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* `data` of `subject.deleted`. A delete through the subjects API carries
|
|
755
|
+
* `subjectId`; a right-to-erasure request carries the tokenized `subjectRef`
|
|
756
|
+
* (with `orgId` / `env`) instead — never the national number.
|
|
757
|
+
* @public
|
|
758
|
+
*/
|
|
759
|
+
interface SubjectDeletedEventData {
|
|
760
|
+
subjectId?: string;
|
|
761
|
+
subjectRef?: string;
|
|
762
|
+
orgId?: string;
|
|
763
|
+
env?: SekyroEnvironment;
|
|
764
|
+
}
|
|
765
|
+
/** Subject lifecycle: a revoked subject cannot be reused. @public */
|
|
766
|
+
type SubjectStatus = "active" | "revoked";
|
|
767
|
+
/**
|
|
768
|
+
* How strongly a subject's identity was established: the reference photo
|
|
769
|
+
* came from the national registry, the document's chip (`chip_biometric`, a
|
|
770
|
+
* passed chip read plus a face match) or the document, and a face match passed
|
|
771
|
+
* (`*_biometric`) or the session had no face match (`*_only`). `supplied_*`:
|
|
772
|
+
* partner-supplied identity data (`input.suppliedIdentity`), with
|
|
773
|
+
* (`supplied_biometric`) or without (`supplied_only`) a face match against its photo.
|
|
774
|
+
* @public
|
|
775
|
+
*/
|
|
776
|
+
type SubjectAssurance = "registry_biometric" | "chip_biometric" | "document_biometric" | "registry_only" | "document_only" | "supplied_biometric" | "supplied_only";
|
|
777
|
+
/**
|
|
778
|
+
* Origin of a subject's stored reference photo, best first (`chip`: the document chip's portrait;
|
|
779
|
+
* `org_supplied`: the photo of your partner-supplied identity data).
|
|
780
|
+
* @public
|
|
781
|
+
*/
|
|
782
|
+
type SubjectReferenceSource = "registry" | "doc_registry" | "chip" | "org_supplied" | "document";
|
|
783
|
+
/** One session linked to a subject. @public */
|
|
784
|
+
interface SubjectSession {
|
|
785
|
+
sessionId: string;
|
|
786
|
+
appId: string | null;
|
|
787
|
+
/** ISO 8601 — when the session was linked. */
|
|
788
|
+
at: string;
|
|
789
|
+
status: SessionStatus;
|
|
790
|
+
outcome: SessionOutcome | null;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* A subject (the same person across your apps), as `GET /subjects/:id`
|
|
794
|
+
* returns it. Never contains photo keys or URLs.
|
|
795
|
+
* @public
|
|
796
|
+
*/
|
|
797
|
+
interface Subject {
|
|
798
|
+
id: string;
|
|
799
|
+
status: SubjectStatus;
|
|
800
|
+
/** ISO 8601 — present only when revoked. */
|
|
801
|
+
revokedAt?: string;
|
|
802
|
+
sessions: SubjectSession[];
|
|
803
|
+
externalRefs: string[];
|
|
804
|
+
/** ISO 8601. */
|
|
805
|
+
firstSeenAt: string;
|
|
806
|
+
/** ISO 8601. */
|
|
807
|
+
lastSeenAt: string;
|
|
808
|
+
/** ISO 8601, or `null` before the first approved session. */
|
|
809
|
+
lastPassedAt: string | null;
|
|
810
|
+
/** ISO 8601 of the last approved full or returning session, or `null`. */
|
|
811
|
+
lastVerifiedAt: string | null;
|
|
812
|
+
/** The stored reference photo's origin and date — `null` until one is stored. */
|
|
813
|
+
reference: {
|
|
814
|
+
source: SubjectReferenceSource;
|
|
815
|
+
capturedAt: string;
|
|
816
|
+
} | null;
|
|
817
|
+
assurance: SubjectAssurance | null;
|
|
818
|
+
/**
|
|
819
|
+
* A returning-user session can be created now: active, a reference photo is
|
|
820
|
+
* stored, and the last FULL verification (returning-user sessions do not
|
|
821
|
+
* reset it) is within your app's
|
|
822
|
+
* `reuse.maxAgeMonths` (when set).
|
|
823
|
+
*/
|
|
824
|
+
returningReady: boolean;
|
|
825
|
+
/** The verified name / birth date of the latest approved session. */
|
|
826
|
+
display?: {
|
|
827
|
+
name?: string;
|
|
828
|
+
birthDate?: string;
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
/** Result of {@link SubjectsApi.delete}. @public */
|
|
832
|
+
interface DeletedSubject {
|
|
833
|
+
id: string;
|
|
834
|
+
deleted: true;
|
|
835
|
+
}
|
|
836
|
+
/** Options of {@link SubjectsApi.revoke}. @public */
|
|
837
|
+
interface RevokeSubjectInput {
|
|
838
|
+
/** Kept on the audit trail only (≤ 500 characters). Do not put personal data here. */
|
|
839
|
+
reason?: string;
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* `data` of `subject.suspicious_activity` — a device already linked to
|
|
843
|
+
* ANOTHER subject was used for a session of this subject. `count` is the
|
|
844
|
+
* subject's running total of such events. Ids only.
|
|
845
|
+
* @public
|
|
846
|
+
*/
|
|
847
|
+
interface SubjectSuspiciousActivityEventData {
|
|
848
|
+
subjectId: string;
|
|
849
|
+
sessionId: string;
|
|
850
|
+
count: number;
|
|
136
851
|
}
|
|
137
852
|
/**
|
|
138
853
|
* `data` of `session.step.completed` — the check that just passed.
|
|
@@ -142,6 +857,21 @@ interface SessionStepCompletedEventData {
|
|
|
142
857
|
sessionId: string;
|
|
143
858
|
checkType: CheckType;
|
|
144
859
|
state: CheckState;
|
|
860
|
+
/** The subject (person) the session is linked to, when known. */
|
|
861
|
+
subjectId?: string;
|
|
862
|
+
/** Your own id for the person, from `subject.externalRef` at create. */
|
|
863
|
+
externalRef?: string;
|
|
864
|
+
/**
|
|
865
|
+
* `true` when an identifier this session read already belongs to a
|
|
866
|
+
* different subject than `subjectId` — the subjects are never merged.
|
|
867
|
+
*/
|
|
868
|
+
subjectConflict?: boolean;
|
|
869
|
+
/** `true` on a returning-user session (liveness + face match against the stored photo). */
|
|
870
|
+
returning?: true;
|
|
871
|
+
/** On a returning-user session: the subject (person) it re-verifies. */
|
|
872
|
+
reuseOf?: string;
|
|
873
|
+
/** When the passed check is `phone_otp` / `email_otp`: that one verified contact. */
|
|
874
|
+
verifiedContacts?: VerifiedContacts;
|
|
145
875
|
}
|
|
146
876
|
/**
|
|
147
877
|
* `data` of `session.review_decided` — a human (or the review-timeout sweep)
|
|
@@ -154,6 +884,21 @@ interface SessionReviewDecidedEventData {
|
|
|
154
884
|
sessionId: string;
|
|
155
885
|
outcome: "pass" | "fail";
|
|
156
886
|
reason: string;
|
|
887
|
+
/** The subject (person) the session is linked to, when known. */
|
|
888
|
+
subjectId?: string;
|
|
889
|
+
/** Your own id for the person, from `subject.externalRef` at create. */
|
|
890
|
+
externalRef?: string;
|
|
891
|
+
/**
|
|
892
|
+
* `true` when an identifier this session read already belongs to a
|
|
893
|
+
* different subject than `subjectId` — the subjects are never merged.
|
|
894
|
+
*/
|
|
895
|
+
subjectConflict?: boolean;
|
|
896
|
+
/** `true` on a returning-user session (liveness + face match against the stored photo). */
|
|
897
|
+
returning?: true;
|
|
898
|
+
/** On a returning-user session: the subject (person) it re-verifies. */
|
|
899
|
+
reuseOf?: string;
|
|
900
|
+
/** ID mode only: the ICAO portrait can be fetched with {@link SessionsApi.portrait}. */
|
|
901
|
+
portraitAvailable?: boolean;
|
|
157
902
|
}
|
|
158
903
|
/**
|
|
159
904
|
* Event type → the shape of its `data`. Session events are typed; the
|
|
@@ -170,8 +915,10 @@ interface WebhookEventDataMap {
|
|
|
170
915
|
"session.rejected": SessionLifecycleEventData;
|
|
171
916
|
"session.expired": SessionLifecycleEventData;
|
|
172
917
|
"session.failed": SessionLifecycleEventData;
|
|
173
|
-
"subject.enrolled":
|
|
174
|
-
"subject.
|
|
918
|
+
"subject.enrolled": SubjectEnrolledEventData;
|
|
919
|
+
"subject.updated": SubjectUpdatedEventData;
|
|
920
|
+
"subject.suspicious_activity": SubjectSuspiciousActivityEventData;
|
|
921
|
+
"subject.deleted": SubjectDeletedEventData;
|
|
175
922
|
"aml.match_flagged": Record<string, unknown>;
|
|
176
923
|
"payment.received": Record<string, unknown>;
|
|
177
924
|
"invoice.generated": Record<string, unknown>;
|
|
@@ -259,18 +1006,66 @@ declare class SessionsApi {
|
|
|
259
1006
|
* surface on purpose (only its bound `call` crosses this boundary).
|
|
260
1007
|
* @internal
|
|
261
1008
|
*/
|
|
262
|
-
constructor(call: <T>(method: "GET" | "POST", path: string, body?: unknown, idempotencyKey?: string) => Promise<T>);
|
|
1009
|
+
constructor(call: <T>(method: "GET" | "POST" | "DELETE", path: string, body?: unknown, idempotencyKey?: string) => Promise<T>);
|
|
263
1010
|
/**
|
|
264
1011
|
* `POST /sessions`. Returns the one-time `clientToken` — hand it to the
|
|
265
1012
|
* browser, never persist it. Pass an `idempotencyKey` so a retried call
|
|
266
1013
|
* cannot create a second session.
|
|
267
1014
|
*/
|
|
268
1015
|
create(input?: CreateSessionInput, opts?: RequestOptions): Promise<CreatedSession>;
|
|
1016
|
+
/**
|
|
1017
|
+
* `POST /sessions/:id/method` — choose the identity method of an
|
|
1018
|
+
* optional-document session on the person's behalf (the hosted method
|
|
1019
|
+
* screen is then skipped). `409 method_locked` once the choice can no
|
|
1020
|
+
* longer change; `409 session_not_active` on a finished or expired session.
|
|
1021
|
+
*/
|
|
1022
|
+
selectMethod(id: string, method: IdentityMethod): Promise<Session>;
|
|
269
1023
|
/** `GET /sessions/:id` — the whitelisted view (no client token). */
|
|
270
1024
|
get(id: string): Promise<Session>;
|
|
1025
|
+
/**
|
|
1026
|
+
* `GET /sessions/:id/portrait?format=url` — ID mode: a 5-minute presigned
|
|
1027
|
+
* link to the 480×640 ICAO portrait (JPEG). Needs the `portrait:read` scope
|
|
1028
|
+
* (keys minted from the dashboard carry it). `404 portrait_not_available`
|
|
1029
|
+
* until the session's `portrait_icao` check has passed (`portraitAvailable`).
|
|
1030
|
+
*/
|
|
1031
|
+
portrait(id: string): Promise<SessionPortraitLink>;
|
|
271
1032
|
}
|
|
272
1033
|
/**
|
|
273
|
-
*
|
|
1034
|
+
* `sekyro.subjects` — read and manage subjects (the same person across your
|
|
1035
|
+
* apps). Reads need the `subjects:read` scope, `revoke` / `delete` the
|
|
1036
|
+
* `subjects:write` scope (keys minted from the dashboard carry both; an older
|
|
1037
|
+
* key answers `403 insufficient_scope`). A foreign or unknown id answers
|
|
1038
|
+
* `404 subject_not_found`.
|
|
1039
|
+
*
|
|
1040
|
+
* Type-only export, like {@link SessionsApi}: obtained through
|
|
1041
|
+
* {@link Sekyro.subjects}.
|
|
1042
|
+
* @public
|
|
1043
|
+
*/
|
|
1044
|
+
declare class SubjectsApi {
|
|
1045
|
+
private readonly call;
|
|
1046
|
+
/** @internal */
|
|
1047
|
+
constructor(call: <T>(method: "GET" | "POST" | "DELETE", path: string, body?: unknown, idempotencyKey?: string) => Promise<T>);
|
|
1048
|
+
/** `GET /subjects/:id` — the subject, its sessions and its returning-user readiness. */
|
|
1049
|
+
get(id: string): Promise<Subject>;
|
|
1050
|
+
/** `GET /subjects?externalRef=…` — the subjects carrying your own id (newest first, at most 50). */
|
|
1051
|
+
list(query: {
|
|
1052
|
+
externalRef: string;
|
|
1053
|
+
}): Promise<Subject[]>;
|
|
1054
|
+
/**
|
|
1055
|
+
* `POST /subjects/:id/revoke` — the subject can no longer be used for
|
|
1056
|
+
* returning-user sessions. Idempotent: revoking again returns the same view.
|
|
1057
|
+
* Emits `subject.updated` with `status: 'revoked'`.
|
|
1058
|
+
*/
|
|
1059
|
+
revoke(id: string, input?: RevokeSubjectInput, opts?: RequestOptions): Promise<Subject>;
|
|
1060
|
+
/**
|
|
1061
|
+
* `DELETE /subjects/:id` — deletes the subject's stored photos, face
|
|
1062
|
+
* templates and identifiers, and unlinks its sessions. Emits
|
|
1063
|
+
* `subject.deleted`. Cannot be undone; a second call answers `404`.
|
|
1064
|
+
*/
|
|
1065
|
+
delete(id: string): Promise<DeletedSubject>;
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Sekyro AI partner API client. Server-side only: it carries your API key.
|
|
274
1069
|
*
|
|
275
1070
|
* Throws {@link SekyroConfigError} synchronously when `apiKey` is missing or
|
|
276
1071
|
* `baseUrl` is not `https:` (plain `http:` is accepted for `localhost`,
|
|
@@ -285,6 +1080,7 @@ declare class SessionsApi {
|
|
|
285
1080
|
*/
|
|
286
1081
|
declare class Sekyro {
|
|
287
1082
|
readonly sessions: SessionsApi;
|
|
1083
|
+
readonly subjects: SubjectsApi;
|
|
288
1084
|
constructor(opts: SekyroOptions);
|
|
289
1085
|
}
|
|
290
1086
|
|
|
@@ -292,7 +1088,7 @@ declare class Sekyro {
|
|
|
292
1088
|
type SekyroWebhookErrorCode = "invalid_signature" | "stale" | "malformed";
|
|
293
1089
|
/**
|
|
294
1090
|
* Thrown by {@link verifyWebhook}. Respond with a 4xx and do not process the
|
|
295
|
-
* body; Sekyro retries deliveries that do not receive a 2xx.
|
|
1091
|
+
* body; Sekyro AI retries deliveries that do not receive a 2xx.
|
|
296
1092
|
* @public
|
|
297
1093
|
*/
|
|
298
1094
|
declare class SekyroWebhookError extends Error {
|
|
@@ -301,7 +1097,7 @@ declare class SekyroWebhookError extends Error {
|
|
|
301
1097
|
constructor(code: SekyroWebhookErrorCode, message?: string);
|
|
302
1098
|
}
|
|
303
1099
|
/** Why the {@link Sekyro} constructor rejected its options. @public */
|
|
304
|
-
type SekyroConfigErrorCode = "missing_api_key" | "invalid_base_url";
|
|
1100
|
+
type SekyroConfigErrorCode = "missing_api_key" | "invalid_base_url" | "invalid_registry_record";
|
|
305
1101
|
/**
|
|
306
1102
|
* Thrown synchronously by `new Sekyro(...)` for an unusable configuration —
|
|
307
1103
|
* before any request is made. Extends `TypeError` so existing
|
|
@@ -345,6 +1141,61 @@ declare class SekyroApiError extends Error {
|
|
|
345
1141
|
constructor(init: SekyroApiErrorInit);
|
|
346
1142
|
}
|
|
347
1143
|
|
|
1144
|
+
/**
|
|
1145
|
+
* The raw ANRPTS `getPersonne` answer your server received with your own
|
|
1146
|
+
* access: `{ personne, photo }`, where `photo` is a base64 JPEG (optionally
|
|
1147
|
+
* data-URL prefixed). Only the fields the mapping reads are typed.
|
|
1148
|
+
* @public
|
|
1149
|
+
*/
|
|
1150
|
+
interface AnrptsPersonneResponse {
|
|
1151
|
+
personne?: {
|
|
1152
|
+
nni?: string | undefined;
|
|
1153
|
+
prenomFr?: string | undefined;
|
|
1154
|
+
patronymeFr?: string | undefined;
|
|
1155
|
+
perePrenomFr?: string | undefined;
|
|
1156
|
+
prenomAr?: string | undefined;
|
|
1157
|
+
patronymeAr?: string | undefined;
|
|
1158
|
+
perePrenomAr?: string | undefined;
|
|
1159
|
+
/** ISO date or date-time (`1990-01-01T00:00:00.000Z`), or `DD/MM/YYYY`. */
|
|
1160
|
+
dateNaissance?: string | undefined;
|
|
1161
|
+
lieuNaissanceFr?: string | undefined;
|
|
1162
|
+
lieuNaissanceAr?: string | undefined;
|
|
1163
|
+
sexeCode?: string | undefined;
|
|
1164
|
+
nationalities?: string[] | undefined;
|
|
1165
|
+
[key: string]: unknown;
|
|
1166
|
+
};
|
|
1167
|
+
photo?: string | undefined;
|
|
1168
|
+
datePhoto?: string | undefined;
|
|
1169
|
+
[key: string]: unknown;
|
|
1170
|
+
}
|
|
1171
|
+
/** Options of {@link registryFromAnrpts}. @public */
|
|
1172
|
+
interface RegistryFromAnrptsOptions {
|
|
1173
|
+
/**
|
|
1174
|
+
* When YOUR system received this ANRPTS answer (not "now" when you map a cached
|
|
1175
|
+
* answer). Required: records older than 24 h are refused at session create.
|
|
1176
|
+
*/
|
|
1177
|
+
fetchedAt: Date | string;
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Convert an ANRPTS `getPersonne` answer that YOUR server already received,
|
|
1181
|
+
* with your own access, into the `input.suppliedIdentity` record of
|
|
1182
|
+
* `sessions.create` (partner-supplied identity data). A pure, local mapping:
|
|
1183
|
+
* it makes no network call, and Sekyro AI never sees, receives or stores any
|
|
1184
|
+
* ANRPTS key — only the record and photo you pass.
|
|
1185
|
+
*
|
|
1186
|
+
* ```ts
|
|
1187
|
+
* const anrpts = await myAnrptsClient.getPersonne(nni); // your call, your key
|
|
1188
|
+
* const suppliedIdentity = registryFromAnrpts(anrpts, { fetchedAt: receivedAt });
|
|
1189
|
+
* await sekyro.sessions.create({ input: { nni, suppliedIdentity } });
|
|
1190
|
+
* ```
|
|
1191
|
+
*
|
|
1192
|
+
* Throws {@link SekyroConfigError} (`invalid_registry_record`) when a required
|
|
1193
|
+
* field (NNI, French and Arabic first and last names, birth date, photo) is
|
|
1194
|
+
* missing. The message names the field, never its value.
|
|
1195
|
+
* @public
|
|
1196
|
+
*/
|
|
1197
|
+
declare function registryFromAnrpts(response: AnrptsPersonneResponse, options: RegistryFromAnrptsOptions): SuppliedIdentityRecord;
|
|
1198
|
+
|
|
348
1199
|
/**
|
|
349
1200
|
* The HTTP header carrying the webhook signature:
|
|
350
1201
|
* `t=<unix seconds>,v1=<hex>[,v1=<hex>]`.
|
|
@@ -352,7 +1203,7 @@ declare class SekyroApiError extends Error {
|
|
|
352
1203
|
*/
|
|
353
1204
|
declare const SEKYRO_SIGNATURE_HEADER = "x-sekyro-signature";
|
|
354
1205
|
/**
|
|
355
|
-
* The pre-rebrand name of the signature header. Sekyro sends BOTH headers with
|
|
1206
|
+
* The pre-rebrand name of the signature header. Sekyro AI sends BOTH headers with
|
|
356
1207
|
* the same value during the transition; prefer {@link SEKYRO_SIGNATURE_HEADER}
|
|
357
1208
|
* and use {@link signatureFromHeaders} to accept either.
|
|
358
1209
|
* @public
|
|
@@ -380,13 +1231,13 @@ interface VerifyWebhookOptions {
|
|
|
380
1231
|
now?: () => number;
|
|
381
1232
|
}
|
|
382
1233
|
/**
|
|
383
|
-
* Verify a Sekyro webhook delivery and return its parsed envelope, discriminated
|
|
1234
|
+
* Verify a Sekyro AI webhook delivery and return its parsed envelope, discriminated
|
|
384
1235
|
* on `event` (narrow with a `switch` to type `data`).
|
|
385
1236
|
*
|
|
386
1237
|
* - `rawBody` MUST be the request body exactly as received (before any JSON
|
|
387
1238
|
* parsing / re-serialisation); the signature covers those bytes.
|
|
388
1239
|
* - Accepts the delivery when ANY `v1` in the header matches, which is what
|
|
389
|
-
* makes secret rotation seamless (Sekyro signs with current + previous).
|
|
1240
|
+
* makes secret rotation seamless (Sekyro AI signs with current + previous).
|
|
390
1241
|
* - Uses `node:crypto` only; the digest compare is constant-time.
|
|
391
1242
|
*
|
|
392
1243
|
* Checks run in this order: header/secret present → header well-formed →
|
|
@@ -401,4 +1252,4 @@ interface VerifyWebhookOptions {
|
|
|
401
1252
|
*/
|
|
402
1253
|
declare function verifyWebhook(rawBody: string, header: string | null | undefined, secret: string, opts?: VerifyWebhookOptions): WebhookEnvelope;
|
|
403
1254
|
|
|
404
|
-
export { type CheckState, type CheckType, type CreateSessionInput, type CreatedSession, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type DeliverOptions, type FetchLike, LEGACY_SIGNATURE_HEADER, type Locale, type RequestOptions, SEKYRO_SIGNATURE_HEADER, Sekyro, SekyroApiError, type SekyroApiErrorInit, type SekyroClientErrorCode, SekyroConfigError, type SekyroConfigErrorCode, type SekyroEnvironment, type SekyroOptions, SekyroWebhookError, type SekyroWebhookErrorCode, type Session, type SessionEventData, type SessionLifecycleEventData, type SessionOutcome, type SessionReviewDecidedEventData, type SessionStatus, type SessionStep, type SessionStepCompletedEventData, SessionsApi, type VerifyWebhookOptions, type WebhookEnvelope, type WebhookEnvelopeOf, type WebhookEventDataMap, type WebhookEventType, signatureFromHeaders, verifyWebhook };
|
|
1255
|
+
export { type AnrptsPersonneResponse, type CheckState, type CheckType, type CreateSessionInput, type CreatedSession, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type DecisionReason, type DeepPartial, type DeletedSubject, type DeliverOptions, type EidResult, type EkycProfileOverride, type EkycProfileSettings, type EligibilityReason, type FetchLike, type IdentityMethod, LEGACY_SIGNATURE_HEADER, type Locale, type MatchReason, type PolicyFailureReason, type PortraitReportEntry, type ProbeBindingReason, type ProbeQuality, type ProfileDocumentType, type ProfileEmail, type RegistryFromAnrptsOptions, type RequestOptions, type RevokeSubjectInput, SEKYRO_SIGNATURE_HEADER, Sekyro, SekyroApiError, type SekyroApiErrorInit, type SekyroClientErrorCode, SekyroConfigError, type SekyroConfigErrorCode, type SekyroEnvironment, type SekyroOptions, SekyroWebhookError, type SekyroWebhookErrorCode, type Session, type SessionDevice, type SessionDeviceSignal, type SessionEventData, type SessionFailureReason, type SessionLifecycleEventData, type SessionNetwork, type SessionNetworkEvent, type SessionOutcome, type SessionPortraitLink, type SessionReviewDecidedEventData, type SessionStatus, type SessionStep, type SessionStepCompletedEventData, type SessionSubjectInput, SessionsApi, type Subject, type SubjectAssurance, type SubjectDeletedEventData, type SubjectEnrolledEventData, type SubjectReferenceSource, type SubjectReuseTarget, type SubjectSession, type SubjectStatus, type SubjectSuspiciousActivityEventData, type SubjectUpdatedEventData, SubjectsApi, type SuppliedIdentityRecord, type VerifiedContacts, type VerifyWebhookOptions, type WebhookEnvelope, type WebhookEnvelopeOf, type WebhookEventDataMap, type WebhookEventType, registryFromAnrpts, signatureFromHeaders, verifyWebhook };
|