@sekyro/node 0.1.0-rc.5 → 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/dist/index.d.ts CHANGED
@@ -1,1211 +1,5 @@
1
- /**
2
- * Wire-level enums and shapes of the Sekyro AI partner API (`/api/v2`). These
3
- * mirror the backend's frozen partner contract; they are plain string unions so
4
- * a newer API value never throws at runtime — it simply widens the type in the
5
- * next SDK release.
6
- *
7
- * @packageDocumentation
8
- */
9
- /** Session lifecycle status. @public */
10
- type SessionStatus = "created" | "in_progress" | "review_pending" | "completed" | "expired" | "failed";
11
- /**
12
- * Terminal verdict, orthogonal to {@link SessionStatus}. `null` until the session
13
- * reaches a terminal status; `review` when a human reviewer must adjudicate.
14
- * @public
15
- */
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";
25
- /** The verification checks a flow may contain. @public */
26
- type CheckType = "liveness" | "face_match" | "doc_ocr" | "population_registry" | "phone_otp" | "nfc_chip" | "email_otp" | "portrait_icao" | "eid_auth";
27
- /** Per-check execution state within a session. @public */
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";
92
- /** Hosted-flow languages. @public */
93
- type Locale = "ar" | "fr" | "en";
94
- /** The environment a webhook was emitted from. @public */
95
- type SekyroEnvironment = "sandbox" | "live";
96
- /**
97
- * One check of a session, as the partner API projects it: enum/id fields only,
98
- * never civil data.
99
- * @public
100
- */
101
- interface SessionStep {
102
- checkType: CheckType;
103
- state: CheckState;
104
- /** Sekyro flows: the flow node this check belongs to (e.g. `n_live`). */
105
- nodeId?: string;
106
- /** Sekyro flows: the service of that node. */
107
- service?: ServiceId;
108
- /** Sekyro flows: the check failed and the flow's fallback took over. */
109
- handled?: true;
110
- /** Machine-readable failure/review reason, when the check has one. */
111
- reason?: string;
112
- /** Calibrated model score in [0, 1] for scored checks (liveness, face match). */
113
- calibratedScore?: number;
114
- /** The check failed after using all of its attempts. */
115
- attemptsExhausted?: boolean;
116
- /**
117
- * `portrait_icao` only (ID mode): the per-criterion ICAO report of the last
118
- * attempt — numbers and enums only, never the image.
119
- */
120
- report?: PortraitReportEntry[];
121
- /**
122
- * `face_match` only: the probe-quality measurement of the selfie that was
123
- * matched (occlusion, head pose, eye openness, sharpness). Recorded for
124
- * information only: it never changes the step's state or reason.
125
- */
126
- probeQuality?: ProbeQuality;
127
- }
128
- /**
129
- * The probe-quality measurement on a `face_match` step. Numbers only; `null`
130
- * means the value could not be measured. Informational: no threshold applies.
131
- * @public
132
- */
133
- interface ProbeQuality {
134
- /** Share of the inner face that is covered (a hand, a mask, a phone), in [0, 1]. */
135
- occlusion: number | null;
136
- /** Head pose in degrees. */
137
- pose: {
138
- yaw: number | null;
139
- pitch: number | null;
140
- roll: number | null;
141
- };
142
- /** Openness of the less-open eye, in [0, 1]. */
143
- eyesOpen: number | null;
144
- /** Sharpness of the face region (higher is sharper; no fixed scale). */
145
- sharpness: number | null;
146
- }
147
- /**
148
- * One row of the `portrait_icao` report (ID mode): the server's measurement of
149
- * one ICAO criterion and the threshold that applied.
150
- * @public
151
- */
152
- interface PortraitReportEntry {
153
- criterion: "pose" | "eyes" | "mouth" | "occlusion" | "glare" | "lighting" | "background" | "resolution";
154
- value: number | null;
155
- threshold: number | string | null;
156
- pass: boolean;
157
- }
158
- /**
159
- * A short-lived link to a session's ICAO portrait (480×640 JPEG), from
160
- * {@link SessionsApi.portrait}. Download it before `expiresAt`; never store the URL.
161
- * @public
162
- */
163
- interface SessionPortraitLink {
164
- url: string;
165
- /** ISO 8601 (about 5 minutes after the call). */
166
- expiresAt: string;
167
- }
168
- /**
169
- * Out-of-band delivery of the session's hosted link, sent by Sekyro AI after the
170
- * session is created. Best effort: a failed send never fails the create.
171
- * @public
172
- */
173
- interface DeliverOptions {
174
- /** Delivery channel. */
175
- channel: "sms" | "whatsapp";
176
- /** E.164 phone number of the recipient, e.g. `+22236123456`. */
177
- to: string;
178
- }
179
- /**
180
- * What you already know about the person (`subject` at session create). A
181
- * closed object: unknown keys are refused (`400`). Every field is optional;
182
- * `null` or blank counts as absent. Identity fields are compared with the
183
- * person's document or registry record when the app's profile turns name
184
- * matching on; `nni` binds the session to that identity.
185
- * @public
186
- */
187
- interface SessionSubjectInput {
188
- /** 10-digit Mauritanian NNI, as a string. */
189
- nni?: string;
190
- /** E.164 phone number, e.g. `+22236123456`. */
191
- phone?: string;
192
- fullName?: string;
193
- /** ISO 8601 date (`YYYY-MM-DD`). */
194
- birthDate?: string;
195
- /** ISO 3166-1 alpha-2, e.g. `MR`. */
196
- nationality?: string;
197
- /** Identity-document number (1–40 characters). */
198
- documentNumber?: string;
199
- /**
200
- * Your own id for this person (1–128 characters). Echoed on the session and
201
- * its webhooks, and searchable on the subjects API.
202
- */
203
- externalRef?: string;
204
- /**
205
- * Returning user (profile v2 step 5): verify this already-verified subject
206
- * again with liveness + face match only, against the reference photo stored
207
- * on the subject. Name exactly one target. Errors at create:
208
- * `404 subject_not_found`, `409 subject_revoked`, `409 subject_no_reference`,
209
- * `409 subject_stale` (older than the flow's `returning.maxAgeMonths`),
210
- * `409 reuse_disabled` (the flow turns returning-user sessions off) and
211
- * `400` / `409 reuse_target_ambiguous`.
212
- */
213
- reuse?: SubjectReuseTarget;
214
- }
215
- /**
216
- * Which subject a returning-user session re-verifies — exactly ONE of the
217
- * subject id, your own `externalRef` (must match a single subject) or the NNI.
218
- * @public
219
- */
220
- type SubjectReuseTarget = {
221
- subjectId: string;
222
- externalRef?: never;
223
- nni?: never;
224
- } | {
225
- externalRef: string;
226
- subjectId?: never;
227
- nni?: never;
228
- } | {
229
- nni: string;
230
- subjectId?: never;
231
- externalRef?: never;
232
- };
233
- /** A document type the backend reads (kyc-docs-v2 vocabulary; `carte_identite` = identity card of any issuer). @public */
234
- type ProfileDocumentType = "carte_identite" | "passport" | "residence_permit";
235
- /**
236
- * The services a Sekyro flow is built from. A session's steps
237
- * ({@link SessionStep.service}) and {@link FlowOverrides.services} use these
238
- * ids; one service may run several checks (e.g. `liveness` with an ICAO
239
- * portrait).
240
- * @public
241
- */
242
- type ServiceId = "device_network" | "phone_otp" | "email_otp" | "national_eid" | "document" | "nfc" | "population_registry" | "supplied_identity" | "summary_confirm" | "liveness" | "face_match" | "aml_screening" | "face_dedup";
243
- /**
244
- * The general settings of a flow that a session may override (the flow's
245
- * `link`, `language` and override policy are not overridable). Country codes
246
- * are ISO 3166-1 alpha-2 (`MR`). Used through {@link FlowOverrides.settings}.
247
- * @public
248
- */
249
- interface FlowOverridableSettings {
250
- /** Who may be verified. */
251
- eligibility: {
252
- /** Accepted nationalities: `'*'` or a list, optionally per IP location. */
253
- nationalities: {
254
- default: "*" | string[];
255
- byLocation?: Record<string, "*" | string[]>;
256
- };
257
- /** Minimum age in years (1–120), or `null` for none. */
258
- minAge: number | null;
259
- };
260
- /** Automatic decisions instead of human review (`retryConfidentRejects` lets confident rejects retry). */
261
- decisions?: {
262
- mode: "automatic" | "review";
263
- retryConfidentRejects: boolean;
264
- };
265
- /** Data consistency across sources. */
266
- fraud: {
267
- /** Comparison of your `subject` data with the reference identity. */
268
- nameMatch?: {
269
- level: "strict" | "standard" | "lenient";
270
- onMismatch: "fail" | "signal";
271
- };
272
- };
273
- /** Returning-user sessions (`subject.reuse`). */
274
- returning: {
275
- /** `false`: returning-user sessions are refused (`409 reuse_disabled`). */
276
- enabled: boolean;
277
- /** Maximum age in months (1–120) of the subject's last verification; `null` = no limit. */
278
- maxAgeMonths: number | null;
279
- };
280
- /** Identity-document rules. */
281
- documents: {
282
- /** Accepted document types: `'*'` (all) or a list, optionally per issuing country. */
283
- accepted: {
284
- default: "*" | ProfileDocumentType[];
285
- byIssuer?: Record<string, ProfileDocumentType[]>;
286
- };
287
- /** Expired documents: refused, allowed, or allowed for `graceDays` (1–365) after expiry. */
288
- expired: "reject" | "allow" | {
289
- graceDays: number;
290
- };
291
- authenticity: "standard" | "off";
292
- };
293
- }
294
- /**
295
- * Document substitution: the portrait a `document` or `nfc` step produces is
296
- * compared with the registry portrait (or the chip with the printed photo).
297
- * `signal` records a risk signal on a clear mismatch, `review` also sends the
298
- * session to review, `off` compares nothing. `failBelow` is the mismatch bound
299
- * in (0, 1), absent = the platform bound.
300
- * @public
301
- */
302
- interface DocumentSubstitutionSettings {
303
- mode: "off" | "signal" | "review";
304
- failBelow?: number;
305
- }
306
- /**
307
- * The per-service settings a session may override, by {@link ServiceId}.
308
- * `attempts` limits are per step (absent = the platform default).
309
- * @public
310
- */
311
- interface FlowServiceSettings {
312
- /** Device & Network Intelligence: IP location, anonymizers, cooldown, device and capture postures. */
313
- device_network: {
314
- /** Where the end user may be (IP location). */
315
- locations: {
316
- mode: "all";
317
- } | {
318
- mode: "only" | "all_except";
319
- countries: string[];
320
- };
321
- /** VPN / proxy / Tor. */
322
- anonymizers: "allow" | "block";
323
- /** Device cooldown after repeated rejections (at most 5 tiers; `[]` = off). */
324
- cooldown?: {
325
- tiers: Array<{
326
- rejectedSessions: number;
327
- windowHours: number;
328
- waitMinutes: number;
329
- }>;
330
- };
331
- /** Device tamper and velocity actions. */
332
- device?: {
333
- tamper: "block" | "signal";
334
- velocity: {
335
- maxIdentitiesPerDevice: number;
336
- windowDays: number;
337
- action: "signal" | "block";
338
- };
339
- };
340
- attestation?: "off" | "signal" | "enforce";
341
- captureIntegrity?: "off" | "signal" | "enforce";
342
- };
343
- phone_otp: {
344
- /** Where the number comes from: your `subject.phone` / `input.phone`, the person, or either. */
345
- numberSource: "prefilled" | "user_entered" | "prefilled_or_entered";
346
- /** Narrow the channels the person may choose from (absent = every channel the org has on). */
347
- channels?: Array<"whatsapp" | "sms">;
348
- /** Which countries codes may be sent to (absent = the flow's allowed `locations`). */
349
- destinations?: "locations" | "any" | {
350
- countries: string[];
351
- };
352
- attempts?: number;
353
- };
354
- email_otp: {
355
- /** Where the address comes from: your `input.email`, the person, or either. */
356
- emailSource: "prefilled" | "user_entered" | "prefilled_or_entered";
357
- /** Disposable (temporary) addresses: allowed, flagged as a risk signal (default), or refused. */
358
- disposable?: "allow" | "signal" | "block";
359
- attempts?: number;
360
- };
361
- national_eid: {
362
- /**
363
- * `'org'` (every national eID the org can use, resolved at session create)
364
- * or provider ids (`{country}.{provider}`, e.g. `MR.houwiyeti`).
365
- */
366
- providers: "org" | string[];
367
- /** The eID portrait is the face-match reference. */
368
- portraitReference: boolean;
369
- };
370
- document: {
371
- attempts?: number;
372
- /** Compare the document photo with the registry photo (absent = the platform default). */
373
- documentSubstitution?: DocumentSubstitutionSettings;
374
- };
375
- /** The chip read (its MRZ source is derived from the flow and is not overridable). */
376
- nfc: {
377
- maxAttempts?: number;
378
- /** Compare the chip photo with the registry photo (absent = the platform default). */
379
- documentSubstitution?: DocumentSubstitutionSettings;
380
- };
381
- population_registry: {
382
- /** `'org'` (every registry the org can use, resolved at session create) or registry ids. */
383
- sources: "org" | Array<"MR.anrpts">;
384
- nniSource: "auto" | "typed" | "bound_only";
385
- onUncovered: "skip";
386
- attempts?: number;
387
- };
388
- supplied_identity: Record<string, never>;
389
- summary_confirm: {
390
- /** Offer « Reprendre » (re-open the identity source). */
391
- retake: boolean;
392
- };
393
- liveness: {
394
- /** `off`, selfie ICAO `alert` / `reject`, or `id_document` (ID-photo portrait, always rejects). */
395
- icao: "off" | "alert" | "reject" | "id_document";
396
- attempts?: number;
397
- };
398
- face_match: {
399
- reference: "auto" | "document" | "registry";
400
- attempts?: number;
401
- };
402
- aml_screening: {
403
- lists: Array<"sanctions" | "pep" | "adverse_media">;
404
- onHit: "review" | "fail";
405
- };
406
- face_dedup: {
407
- scope: "app" | "org";
408
- onDuplicate: "review" | "fail";
409
- };
410
- }
411
- /**
412
- * `T` with every property optional, recursively. Arrays are replaced as a
413
- * whole, never merged element by element.
414
- * @public
415
- */
416
- type DeepPartial<T> = T extends readonly unknown[] ? T : T extends object ? {
417
- [K in keyof T]?: DeepPartial<T[K]>;
418
- } : T;
419
- /**
420
- * A per-session override of the flow ({@link CreateSessionInput.overrides}).
421
- *
422
- * - `settings`: any subset of {@link FlowOverridableSettings}, at any depth.
423
- * - `services`: keyed by service (applied to every node of that service): any
424
- * subset of its {@link FlowServiceSettings}, or `'off'` to remove it from
425
- * the flow. A service the flow does not use is ignored.
426
- *
427
- * Objects merge into the flow; arrays and scalar values replace. Unknown keys
428
- * and invalid results answer `400 override_invalid`. An override may only
429
- * make the flow stricter unless the flow allows widening; turning a service
430
- * `'off'` always widens. A widening change answers `403 override_widens_flow`.
431
- * The changed paths are reported on {@link Session.overriddenFields}.
432
- *
433
- * @example
434
- * ```ts
435
- * await sekyro.sessions.create({
436
- * flow: "foreigners",
437
- * overrides: {
438
- * settings: { eligibility: { minAge: 21 } },
439
- * services: { liveness: { attempts: 2 } },
440
- * },
441
- * });
442
- * ```
443
- * @public
444
- */
445
- interface FlowOverrides {
446
- settings?: DeepPartial<FlowOverridableSettings>;
447
- services?: {
448
- [S in ServiceId]?: DeepPartial<FlowServiceSettings[S]> | "off";
449
- };
450
- }
451
- /**
452
- * The flow version a session runs: its `flw_` id, its slug and the published
453
- * version pinned at create.
454
- * @public
455
- */
456
- interface SessionFlowRef {
457
- id: string;
458
- slug: string;
459
- version: number;
460
- }
461
- /**
462
- * One flow of your app, from {@link Sekyro.listFlows}. Published flows only;
463
- * pass `slug` or `id` as {@link CreateSessionInput.flow}.
464
- * @public
465
- */
466
- interface FlowSummary {
467
- /** `flw_` id. */
468
- id: string;
469
- slug: string;
470
- name: string;
471
- /** The published version a new session runs. */
472
- version: number;
473
- /** The flow a session runs when you send no `flow`. */
474
- isDefault: boolean;
475
- }
476
- /**
477
- * Input of {@link SessionsApi.create}. The API key belongs to an app; the
478
- * app's flow (its default flow, or the one named in `flow`) decides the steps.
479
- * @public
480
- */
481
- interface CreateSessionInput {
482
- /**
483
- * The app's flow to run: its slug (e.g. `foreigners`) or `flw_` id, as
484
- * {@link Sekyro.listFlows} lists them. Absent = the app's default flow.
485
- * Errors: `404 flow_not_found` (no such flow in the key's app),
486
- * `409 flow_not_published`, `409 flow_archived`.
487
- */
488
- flow?: string;
489
- /** Pins the hosted-flow language. Omit to let the person's device decide. */
490
- locale?: Locale;
491
- /**
492
- * Pre-choose the identity method when the flow offers a choice of identity
493
- * method (the hosted choice screen is then skipped). Ignored otherwise.
494
- */
495
- identityMethod?: IdentityMethod;
496
- /**
497
- * Session lifetime in minutes. Must fall within the flow's link range,
498
- * otherwise the API answers `400 ttl_out_of_policy`. Omit to use the
499
- * flow's default.
500
- */
501
- ttlMinutes?: number;
502
- /** Also send the hosted link to the person by SMS or WhatsApp. */
503
- deliver?: DeliverOptions;
504
- /**
505
- * What you already know about the person (preferred over `input` for
506
- * identity data). A key sent in both `subject` and `input` with different
507
- * values answers `400 subject_input_conflict`.
508
- */
509
- subject?: SessionSubjectInput;
510
- /**
511
- * Override the flow for this session only ({@link FlowOverrides}): its
512
- * general settings and per-service settings, or turn a service `'off'`.
513
- * `400 override_invalid` for an unknown key or an invalid result;
514
- * `403 override_widens_flow` for a change that makes the flow less strict
515
- * when the flow does not allow widening.
516
- */
517
- overrides?: FlowOverrides;
518
- /**
519
- * Step inputs consumed server-side. The known anchors are typed (all
520
- * strings — an `nni` is a 10-digit string, never a number); further keys
521
- * pass through to the API as-is. `nni` binds the session to that identity:
522
- * the hosted flow may omit it but cannot substitute another.
523
- */
524
- input?: {
525
- /** 10-digit Mauritanian NNI, as a string. */
526
- nni?: string;
527
- /** E.164 phone number, e.g. `+22236123456`. */
528
- phone?: string;
529
- /** Email address for the email OTP step (`emailSource` `prefilled` / `prefilled_or_entered`). */
530
- email?: string;
531
- fullName?: string;
532
- /** ISO 8601 date (`YYYY-MM-DD`). */
533
- birthDate?: string;
534
- /**
535
- * Partner-supplied identity data (`supplied` mode only): the person's
536
- * biographic record and photo as YOUR system holds them — for example
537
- * from your own ANRPTS answer, converted with {@link registryFromAnrpts}.
538
- * Sekyro AI stores it as data you vouch for, makes no registry call, and
539
- * never receives or stores any registry key. `nni` must be set too and
540
- * equal `suppliedIdentity.nni`. Refused unless Sekyro AI enabled the mode
541
- * for the environment (`403 supplied_identity_not_entitled`); required in
542
- * that mode when the app verifies identity (`422 supplied_identity_required`).
543
- */
544
- suppliedIdentity?: SuppliedIdentityRecord;
545
- } & Record<string, unknown>;
546
- }
547
- /**
548
- * Partner-supplied identity data (`sessions.create` `input.suppliedIdentity`):
549
- * the person's biographic record and photo, as your organization holds them.
550
- * Sekyro AI treats it as identity data supplied by your organization, not as
551
- * a registry lookup. Refusals at create: `400 supplied_identity_nni_mismatch`,
552
- * `422 supplied_identity_stale` (`fetchedAt` older than 24 h),
553
- * `422 supplied_identity_photo_unusable` (not a JPEG/PNG of at most 1 MB within the
554
- * size bounds). In a `supplied` environment whose app verifies identity, a create
555
- * without `input.suppliedIdentity` is `422 supplied_identity_required`. The face on
556
- * the photo is checked after consent, at the face match (`no_face_reference`).
557
- * @public
558
- */
559
- interface SuppliedIdentityRecord {
560
- /** 10-digit NNI, as a string. */
561
- nni: string;
562
- firstNameFr: string;
563
- lastNameFr: string;
564
- fatherNameFr?: string;
565
- firstNameAr: string;
566
- lastNameAr: string;
567
- fatherNameAr?: string;
568
- /** ISO 8601 date (`YYYY-MM-DD`). */
569
- birthDate: string;
570
- birthPlaceFr?: string;
571
- birthPlaceAr?: string;
572
- sex?: "M" | "F";
573
- /** Nationality text as your source holds it (e.g. `Mauritanienne`). */
574
- nationalities?: string[];
575
- /** Base64 JPEG or PNG (at most 1 MB decoded). */
576
- photo: string;
577
- /** ISO 8601 date (`YYYY-MM-DD`) of the registry photo. */
578
- photoDate?: string;
579
- /** ISO 8601 date-time at which your system obtained this record. */
580
- fetchedAt: string;
581
- }
582
- /** Per-request options. @public */
583
- interface RequestOptions {
584
- /**
585
- * Sent as `X-Idempotency-Key`. Replaying the same key within 24 h returns the
586
- * original response instead of creating a second session.
587
- */
588
- idempotencyKey?: string;
589
- }
590
- /**
591
- * Result of {@link SessionsApi.create}. `clientToken` is returned ONCE and never
592
- * again — hand it to the browser (`@sekyro/web`) and do not persist it.
593
- * @public
594
- */
595
- interface CreatedSession {
596
- id: string;
597
- status: SessionStatus;
598
- /** One-time credential for the client SDK / hosted flow. */
599
- clientToken: string;
600
- /** Hosted verification link for this session. */
601
- hostedUrl: string;
602
- /** The flow version the session runs (the default flow when you sent no `flow`). */
603
- flow?: SessionFlowRef;
604
- /** The checks on the session's path (checks of a lane not taken are omitted). */
605
- steps: SessionStep[];
606
- /** ISO 8601 — when the session (and its link) stop accepting input. */
607
- expiresAt: string;
608
- }
609
- /**
610
- * The end user's network at the start of the session (IP-derived, approximate).
611
- * @public
612
- */
613
- interface SessionNetworkEvent {
614
- /** ISO 8601. */
615
- at: string;
616
- /** ISO 3166-1 alpha-2. */
617
- country?: string;
618
- region?: string;
619
- city?: string;
620
- asn?: number;
621
- operator?: string;
622
- connectionType: "mobile" | "hosting" | "unknown";
623
- anonymizer: {
624
- vpn: boolean;
625
- tor: boolean;
626
- hosting: boolean;
627
- };
628
- /** IANA time zone reported by the end user's device. */
629
- browserTimezone?: string;
630
- /** True when the device time zone does not belong to the IP country. */
631
- timezoneMismatch?: boolean;
632
- }
633
- /**
634
- * End-user network context; absent when not recorded.
635
- * @public
636
- */
637
- interface SessionNetwork {
638
- /** The start event's IP address. Never sent in webhooks. */
639
- ip?: string;
640
- /** True when a later call came from a different country than the start. */
641
- countryChanged: boolean;
642
- start: SessionNetworkEvent;
643
- }
644
- /**
645
- * Device signals detected on the end user's device.
646
- * @public
647
- */
648
- type SessionDeviceSignal = "webdriver" | "headless" | "emulator" | "rooted" | "jailbroken" | "debugger" | "hooking" | "virtual_camera";
649
- /**
650
- * End-user device summary; absent when not recorded.
651
- * @public
652
- */
653
- interface SessionDevice {
654
- platform: "web" | "android" | "ios";
655
- sdk?: string;
656
- os?: string;
657
- osVersion?: string;
658
- browser?: string;
659
- browserVersion?: string;
660
- deviceClass: "mobile" | "tablet" | "desktop" | "unknown";
661
- model?: string;
662
- /** Device ids hashed with a key unique to your organization. */
663
- deviceKeys: string[];
664
- signals: SessionDeviceSignal[];
665
- }
666
- /**
667
- * The national eID that verified the session's identity (the `eid_auth` check
668
- * passed): the provider id (`{country}.{provider}`, e.g. `MR.houwiyeti`), its
669
- * ISO 3166-1 alpha-2 country and when. A passed eID ends the session.
670
- * @public
671
- */
672
- interface EidResult {
673
- provider: string;
674
- country: string;
675
- verifiedAt: string;
676
- }
677
- /**
678
- * The full phone number and email address the person verified in a session
679
- * (an OTP check that passed) — sent ONLY to you (the session view and your
680
- * webhooks), never to the person's browser, and removed when the session's
681
- * personal data is purged. You are the controller of these values. A
682
- * prefilled value that was not verified is never listed.
683
- * @public
684
- */
685
- interface VerifiedContacts {
686
- /** From a passed `phone_otp` check. */
687
- phone?: {
688
- /** E.164, e.g. `+22236123456`. */
689
- number: string;
690
- /** The channel the code was delivered on. */
691
- channel: "whatsapp" | "sms";
692
- /** ISO 8601. */
693
- verifiedAt: string;
694
- };
695
- /** From a passed `email_otp` check. */
696
- email?: {
697
- /** The verified address (domain lowercased). */
698
- address: string;
699
- /** ISO 8601. */
700
- verifiedAt: string;
701
- };
702
- }
703
- /**
704
- * The whitelisted session view returned by {@link SessionsApi.get}. Never
705
- * contains the client token.
706
- * @public
707
- */
708
- interface Session {
709
- id: string;
710
- /** Sekyro flows: the flow version the session runs (absent on sessions created before flows). */
711
- flow?: SessionFlowRef;
712
- status: SessionStatus;
713
- outcome: SessionOutcome | null;
714
- /** The checks on the session's path (checks of a lane the person did not take are omitted). */
715
- steps: SessionStep[];
716
- /** ISO 8601. */
717
- expiresAt: string;
718
- /** ISO 8601. */
719
- createdAt: string;
720
- /** End-user network context; absent when not recorded. */
721
- network?: SessionNetwork;
722
- /** End-user device; absent when not recorded. */
723
- device?: SessionDevice;
724
- /**
725
- * Set when a profile-v2 policy ended the session: a session-level gate
726
- * (location, anonymizer) or a failed check's policy reason (eligibility,
727
- * `document_nni_mismatch`). Absent otherwise.
728
- */
729
- failureReason?: PolicyFailureReason;
730
- /** The subject (person) this session is linked to, once an identifier is known. */
731
- subjectId?: string;
732
- /** Your own id for the person, from `subject.externalRef` at create. */
733
- externalRef?: string;
734
- /**
735
- * `true` when an identifier this session read already belongs to a
736
- * different subject than `subjectId` — the subjects are never merged.
737
- */
738
- subjectConflict?: boolean;
739
- /**
740
- * ISO 8601 — when the end user's device may verify again; set with
741
- * `failureReason: 'device_cooldown'`.
742
- */
743
- retryAfter?: string;
744
- /**
745
- * Dotted paths this session's {@link CreateSessionInput.overrides} changed
746
- * (e.g. `settings.decisions.mode`, `services.liveness`).
747
- */
748
- overriddenFields?: string[];
749
- /**
750
- * Risk signals recorded on the session (e.g. `device_velocity`,
751
- * `timezone_mismatch`, `icao_non_compliant` when an `alert` ICAO policy flagged
752
- * the selfie) — signals only, never a verdict by themselves.
753
- */
754
- riskSignals?: string[];
755
- /**
756
- * `true` for a returning-user session (created with
757
- * {@link SessionSubjectInput.reuse}): liveness + face match against the
758
- * subject's stored reference photo only.
759
- */
760
- returning?: true;
761
- /** On a returning-user session: the subject (person) it re-verifies. */
762
- reuseOf?: string;
763
- /** The contacts verified in this session (passed OTP checks only); absent when none. */
764
- verifiedContacts?: VerifiedContacts;
765
- /** The national eID that verified this session (the `eid_auth` check passed); absent otherwise. */
766
- eid?: EidResult;
767
- /**
768
- * ID mode (`portrait.purpose = id_document`) only: the ICAO portrait can be
769
- * fetched with {@link SessionsApi.portrait}.
770
- */
771
- portraitAvailable?: boolean;
772
- }
773
- /**
774
- * `data` of the terminal / review-pending session lifecycle events
775
- * (`session.completed` | `session.failed` | `session.review_pending` |
776
- * `session.expired` | `session.rejected`) — ids and enums only, no civil data,
777
- * scores or links. `outcome` and `steps` travel on `completed` / `failed`;
778
- * `review_pending` carries `steps` without an outcome; `expired` carries the
779
- * status and the session's refs (`flow`, subject ids, `overriddenFields`).
780
- * @public
781
- */
782
- interface SessionLifecycleEventData {
783
- sessionId: string;
784
- status: SessionStatus;
785
- outcome?: SessionOutcome | null;
786
- /** The checks on the session's path (checks of a lane the person did not take are omitted). */
787
- steps?: Array<Pick<SessionStep, "checkType" | "state" | "nodeId" | "service">>;
788
- /** Sekyro flows: the flow version the session runs (absent on sessions created before flows). */
789
- flow?: SessionFlowRef;
790
- /** Machine-readable reason, when the transition has one. */
791
- reason?: string;
792
- /** End-user network context (no IP); on `completed` / `failed` when recorded. */
793
- network?: Omit<SessionNetwork, "ip">;
794
- /** End-user device; on `completed` / `failed` when recorded. */
795
- device?: SessionDevice;
796
- /**
797
- * On `session.failed`: set when a profile-v2 policy ended the session — a
798
- * session-level gate (location, anonymizer) or a failed check's policy
799
- * reason (eligibility, `document_nni_mismatch`).
800
- */
801
- failureReason?: PolicyFailureReason;
802
- /** The subject (person) the session is linked to, when known. */
803
- subjectId?: string;
804
- /** Your own id for the person, from `subject.externalRef` at create. */
805
- externalRef?: string;
806
- /**
807
- * `true` when an identifier this session read already belongs to a
808
- * different subject than `subjectId` — the subjects are never merged.
809
- */
810
- subjectConflict?: boolean;
811
- /** `true` on a returning-user session (liveness + face match against the stored photo). */
812
- returning?: true;
813
- /** On a returning-user session: the subject (person) it re-verifies. */
814
- reuseOf?: string;
815
- /**
816
- * On `session.failed` with `failureReason: 'device_cooldown'`: ISO time
817
- * from which the device may start a new verification.
818
- */
819
- retryAfter?: string;
820
- /** Dotted paths the session's create-time {@link CreateSessionInput.overrides} changed. */
821
- overriddenFields?: string[];
822
- /** On `completed` / `failed`: the contacts verified in this session (passed OTP checks only). */
823
- verifiedContacts?: VerifiedContacts;
824
- /** On `completed` / `failed`: the national eID that verified this session, when one did. */
825
- eid?: EidResult;
826
- /**
827
- * ID mode only, on `completed` / `failed`: the ICAO portrait can be fetched
828
- * with {@link SessionsApi.portrait} (the event never carries the image).
829
- */
830
- portraitAvailable?: boolean;
831
- }
832
- /**
833
- * `data` of `subject.updated` — either a session linked to the subject reached
834
- * a terminal state (`sessionId`, session `status`, `outcome`), or the subject
835
- * was revoked (`status: 'revoked'`, no session). Ids and enums only.
836
- * @public
837
- */
838
- interface SubjectUpdatedEventData {
839
- subjectId: string;
840
- /** The session that reached a terminal state; absent on a revocation. */
841
- sessionId?: string;
842
- status: SessionStatus | "revoked";
843
- /** The session outcome; absent on a revocation. */
844
- outcome?: SessionOutcome | null;
845
- }
846
- /**
847
- * `data` of `subject.enrolled` — the subject's first reference photo was
848
- * stored (it can now be used for returning-user sessions). Ids and enums only.
849
- * @public
850
- */
851
- interface SubjectEnrolledEventData {
852
- subjectId: string;
853
- /** The approved session the reference photo came from. */
854
- sessionId: string;
855
- assurance: SubjectAssurance;
856
- }
857
- /**
858
- * `data` of `subject.deleted`. A delete through the subjects API carries
859
- * `subjectId`; a right-to-erasure request carries the tokenized `subjectRef`
860
- * (with `orgId` / `env`) instead — never the national number.
861
- * @public
862
- */
863
- interface SubjectDeletedEventData {
864
- subjectId?: string;
865
- subjectRef?: string;
866
- orgId?: string;
867
- env?: SekyroEnvironment;
868
- }
869
- /** Subject lifecycle: a revoked subject cannot be reused. @public */
870
- type SubjectStatus = "active" | "revoked";
871
- /**
872
- * How strongly a subject's identity was established: the reference photo
873
- * came from the national registry, the document's chip (`chip_biometric`, a
874
- * passed chip read plus a face match) or the document, and a face match passed
875
- * (`*_biometric`) or the session had no face match (`*_only`). `supplied_*`:
876
- * partner-supplied identity data (`input.suppliedIdentity`), with
877
- * (`supplied_biometric`) or without (`supplied_only`) a face match against its photo.
878
- * @public
879
- */
880
- type SubjectAssurance = "registry_biometric" | "chip_biometric" | "document_biometric" | "registry_only" | "document_only" | "supplied_biometric" | "supplied_only";
881
- /**
882
- * Origin of a subject's stored reference photo, best first (`chip`: the document chip's portrait;
883
- * `org_supplied`: the photo of your partner-supplied identity data).
884
- * @public
885
- */
886
- type SubjectReferenceSource = "registry" | "doc_registry" | "chip" | "org_supplied" | "document";
887
- /** One session linked to a subject. @public */
888
- interface SubjectSession {
889
- sessionId: string;
890
- appId: string | null;
891
- /** ISO 8601 — when the session was linked. */
892
- at: string;
893
- status: SessionStatus;
894
- outcome: SessionOutcome | null;
895
- }
896
- /**
897
- * A subject (the same person across your apps), as `GET /subjects/:id`
898
- * returns it. Never contains photo keys or URLs.
899
- * @public
900
- */
901
- interface Subject {
902
- id: string;
903
- status: SubjectStatus;
904
- /** ISO 8601 — present only when revoked. */
905
- revokedAt?: string;
906
- sessions: SubjectSession[];
907
- externalRefs: string[];
908
- /** ISO 8601. */
909
- firstSeenAt: string;
910
- /** ISO 8601. */
911
- lastSeenAt: string;
912
- /** ISO 8601, or `null` before the first approved session. */
913
- lastPassedAt: string | null;
914
- /** ISO 8601 of the last approved full or returning session, or `null`. */
915
- lastVerifiedAt: string | null;
916
- /** The stored reference photo's origin and date — `null` until one is stored. */
917
- reference: {
918
- source: SubjectReferenceSource;
919
- capturedAt: string;
920
- } | null;
921
- assurance: SubjectAssurance | null;
922
- /**
923
- * A returning-user session can be created now: active, a reference photo is
924
- * stored, and the last FULL verification (returning-user sessions do not
925
- * reset it) is within the returning-user limit of your app's default flow
926
- * (`settings.returning`: turned on, and `maxAgeMonths` when set).
927
- */
928
- returningReady: boolean;
929
- /** The verified name / birth date of the latest approved session. */
930
- display?: {
931
- name?: string;
932
- birthDate?: string;
933
- };
934
- }
935
- /** Result of {@link SubjectsApi.delete}. @public */
936
- interface DeletedSubject {
937
- id: string;
938
- deleted: true;
939
- }
940
- /** Options of {@link SubjectsApi.revoke}. @public */
941
- interface RevokeSubjectInput {
942
- /** Kept on the audit trail only (≤ 500 characters). Do not put personal data here. */
943
- reason?: string;
944
- }
945
- /**
946
- * `data` of `subject.suspicious_activity` — a device already linked to
947
- * ANOTHER subject was used for a session of this subject. `count` is the
948
- * subject's running total of such events. Ids only.
949
- * @public
950
- */
951
- interface SubjectSuspiciousActivityEventData {
952
- subjectId: string;
953
- sessionId: string;
954
- count: number;
955
- }
956
- /**
957
- * `data` of `session.step.completed` — the check that just passed.
958
- * @public
959
- */
960
- interface SessionStepCompletedEventData {
961
- sessionId: string;
962
- checkType: CheckType;
963
- state: CheckState;
964
- /** Sekyro flows: the flow node of the passed check. */
965
- nodeId?: string;
966
- /** Sekyro flows: the service of that node. */
967
- service?: ServiceId;
968
- /** Sekyro flows: the flow version the session runs (absent on sessions created before flows). */
969
- flow?: SessionFlowRef;
970
- /** The subject (person) the session is linked to, when known. */
971
- subjectId?: string;
972
- /** Your own id for the person, from `subject.externalRef` at create. */
973
- externalRef?: string;
974
- /**
975
- * `true` when an identifier this session read already belongs to a
976
- * different subject than `subjectId` — the subjects are never merged.
977
- */
978
- subjectConflict?: boolean;
979
- /** `true` on a returning-user session (liveness + face match against the stored photo). */
980
- returning?: true;
981
- /** On a returning-user session: the subject (person) it re-verifies. */
982
- reuseOf?: string;
983
- /** When the passed check is `phone_otp` / `email_otp`: that one verified contact. */
984
- verifiedContacts?: VerifiedContacts;
985
- }
986
- /**
987
- * `data` of `session.review_decided` — a human (or the review-timeout sweep)
988
- * decided a pending review. `outcome` speaks the REVIEW vocabulary
989
- * (`pass` | `fail`), not the session outcome; `reason` is the reviewer's code
990
- * (e.g. `review_timeout` for an expired review).
991
- * @public
992
- */
993
- interface SessionReviewDecidedEventData {
994
- sessionId: string;
995
- outcome: "pass" | "fail";
996
- reason: string;
997
- /** Sekyro flows: the flow version the session runs (absent on sessions created before flows). */
998
- flow?: SessionFlowRef;
999
- /** The subject (person) the session is linked to, when known. */
1000
- subjectId?: string;
1001
- /** Your own id for the person, from `subject.externalRef` at create. */
1002
- externalRef?: string;
1003
- /**
1004
- * `true` when an identifier this session read already belongs to a
1005
- * different subject than `subjectId` — the subjects are never merged.
1006
- */
1007
- subjectConflict?: boolean;
1008
- /** `true` on a returning-user session (liveness + face match against the stored photo). */
1009
- returning?: true;
1010
- /** On a returning-user session: the subject (person) it re-verifies. */
1011
- reuseOf?: string;
1012
- /** ID mode only: the ICAO portrait can be fetched with {@link SessionsApi.portrait}. */
1013
- portraitAvailable?: boolean;
1014
- }
1015
- /**
1016
- * Event type → the shape of its `data`. Session events are typed; the
1017
- * remaining events carry an open record until their payloads are frozen.
1018
- * @public
1019
- */
1020
- interface WebhookEventDataMap {
1021
- "session.created": Record<string, unknown>;
1022
- "session.started": Record<string, unknown>;
1023
- "session.step.completed": SessionStepCompletedEventData;
1024
- "session.review_pending": SessionLifecycleEventData;
1025
- "session.review_decided": SessionReviewDecidedEventData;
1026
- "session.completed": SessionLifecycleEventData;
1027
- "session.rejected": SessionLifecycleEventData;
1028
- "session.expired": SessionLifecycleEventData;
1029
- "session.failed": SessionLifecycleEventData;
1030
- "subject.enrolled": SubjectEnrolledEventData;
1031
- "subject.updated": SubjectUpdatedEventData;
1032
- "subject.suspicious_activity": SubjectSuspiciousActivityEventData;
1033
- "subject.deleted": SubjectDeletedEventData;
1034
- "aml.match_flagged": Record<string, unknown>;
1035
- "payment.received": Record<string, unknown>;
1036
- "invoice.generated": Record<string, unknown>;
1037
- "invoice.paid": Record<string, unknown>;
1038
- "invoice.overdue": Record<string, unknown>;
1039
- "webhook.test": Record<string, unknown>;
1040
- }
1041
- /** Every event a partner endpoint may receive. @public */
1042
- type WebhookEventType = keyof WebhookEventDataMap;
1043
- /**
1044
- * The signed webhook payload for ONE event type. There is no global API
1045
- * version; each event carries its own per-event `version`. Retried deliveries
1046
- * keep the same `id` — de-duplicate on it.
1047
- * @public
1048
- */
1049
- interface WebhookEnvelopeOf<E extends WebhookEventType> {
1050
- /** UUIDv7, stable across delivery retries. */
1051
- id: string;
1052
- event: E;
1053
- /** Per-event payload version. */
1054
- version: number;
1055
- /** ISO 8601. */
1056
- createdAt: string;
1057
- orgId: string;
1058
- env: SekyroEnvironment;
1059
- data: WebhookEventDataMap[E];
1060
- }
1061
- /**
1062
- * The signed webhook payload, discriminated on `event`: narrowing on
1063
- * `envelope.event` narrows `envelope.data` to that event's shape.
1064
- *
1065
- * @example
1066
- * ```ts
1067
- * const envelope = verifyWebhook(rawBody, header, secret);
1068
- * switch (envelope.event) {
1069
- * case "session.completed":
1070
- * envelope.data.outcome; // SessionOutcome | null | undefined
1071
- * break;
1072
- * case "session.review_decided":
1073
- * envelope.data.outcome; // "pass" | "fail"
1074
- * break;
1075
- * }
1076
- * ```
1077
- * @public
1078
- */
1079
- type WebhookEnvelope<E extends WebhookEventType = WebhookEventType> = E extends WebhookEventType ? WebhookEnvelopeOf<E> : never;
1080
- /**
1081
- * `data` of any `session.*` event.
1082
- * @deprecated Use {@link SessionLifecycleEventData}, {@link SessionStepCompletedEventData}
1083
- * or {@link SessionReviewDecidedEventData}, or narrow a {@link WebhookEnvelope} on `event`.
1084
- * @public
1085
- */
1086
- type SessionEventData = SessionLifecycleEventData | SessionStepCompletedEventData | SessionReviewDecidedEventData;
1087
-
1088
- /** Default partner API origin + version prefix. @public */
1089
- declare const DEFAULT_BASE_URL = "https://api.sekyro.ai/api/v2";
1090
- /** Default per-request timeout. @public */
1091
- declare const DEFAULT_TIMEOUT_MS = 30000;
1092
- /** `fetch`-compatible function; the global `fetch` by default. @public */
1093
- type FetchLike = typeof globalThis.fetch;
1094
- /** Constructor options of {@link Sekyro}. @public */
1095
- interface SekyroOptions {
1096
- /** Partner API key (server-side only). Sent as `Authorization: Bearer`. */
1097
- apiKey: string;
1098
- /** API base URL incl. the `/api/v2` prefix. Default {@link DEFAULT_BASE_URL}. */
1099
- baseUrl?: string;
1100
- /** Per-request timeout in ms. Default {@link DEFAULT_TIMEOUT_MS}. */
1101
- timeoutMs?: number;
1102
- /** Custom `fetch` (tests, proxies, custom agents). Default `globalThis.fetch`. */
1103
- fetch?: FetchLike;
1104
- }
1105
- /**
1106
- * `sekyro.sessions` — create and read verification sessions.
1107
- *
1108
- * Exported as a TYPE only (`import type { SessionsApi }`): the class value is
1109
- * not part of the package surface, so it can only be obtained through
1110
- * {@link Sekyro.sessions} — never constructed by hand with an unchecked
1111
- * transport.
1112
- * @public
1113
- */
1114
- declare class SessionsApi {
1115
- private readonly call;
1116
- /**
1117
- * Constructed by {@link Sekyro}; the transport is kept out of the public
1118
- * surface on purpose (only its bound `call` crosses this boundary).
1119
- * @internal
1120
- */
1121
- constructor(call: <T>(method: "GET" | "POST" | "DELETE", path: string, body?: unknown, idempotencyKey?: string) => Promise<T>);
1122
- /**
1123
- * `POST /sessions`. Returns the one-time `clientToken` — hand it to the
1124
- * browser, never persist it. Pass an `idempotencyKey` so a retried call
1125
- * cannot create a second session.
1126
- */
1127
- create(input?: CreateSessionInput, opts?: RequestOptions): Promise<CreatedSession>;
1128
- /**
1129
- * `POST /sessions/:id/method` — choose the identity method of an
1130
- * optional-document session on the person's behalf (the hosted method
1131
- * screen is then skipped). `409 method_locked` once the choice can no
1132
- * longer change; `409 session_not_active` on a finished or expired session.
1133
- * On a flow session: `409 choice_not_found` (the flow has no identity-method
1134
- * choice), `409 choice_locked`, `409 choice_option_unavailable` (see
1135
- * {@link isFlowError}).
1136
- */
1137
- selectMethod(id: string, method: IdentityMethod): Promise<Session>;
1138
- /** `GET /sessions/:id` — the whitelisted view (no client token). */
1139
- get(id: string): Promise<Session>;
1140
- /**
1141
- * `GET /sessions/:id/portrait?format=url` — ID mode: a 5-minute presigned
1142
- * link to the 480×640 ICAO portrait (JPEG). Needs the `portrait:read` scope
1143
- * (keys minted from the dashboard carry it). `404 portrait_not_available`
1144
- * until the session's `portrait_icao` check has passed (`portraitAvailable`).
1145
- */
1146
- portrait(id: string): Promise<SessionPortraitLink>;
1147
- }
1148
- /**
1149
- * `sekyro.subjects` — read and manage subjects (the same person across your
1150
- * apps). Reads need the `subjects:read` scope, `revoke` / `delete` the
1151
- * `subjects:write` scope (keys minted from the dashboard carry both; an older
1152
- * key answers `403 insufficient_scope`). A foreign or unknown id answers
1153
- * `404 subject_not_found`.
1154
- *
1155
- * Type-only export, like {@link SessionsApi}: obtained through
1156
- * {@link Sekyro.subjects}.
1157
- * @public
1158
- */
1159
- declare class SubjectsApi {
1160
- private readonly call;
1161
- /** @internal */
1162
- constructor(call: <T>(method: "GET" | "POST" | "DELETE", path: string, body?: unknown, idempotencyKey?: string) => Promise<T>);
1163
- /** `GET /subjects/:id` — the subject, its sessions and its returning-user readiness. */
1164
- get(id: string): Promise<Subject>;
1165
- /** `GET /subjects?externalRef=…` — the subjects carrying your own id (newest first, at most 50). */
1166
- list(query: {
1167
- externalRef: string;
1168
- }): Promise<Subject[]>;
1169
- /**
1170
- * `POST /subjects/:id/revoke` — the subject can no longer be used for
1171
- * returning-user sessions. Idempotent: revoking again returns the same view.
1172
- * Emits `subject.updated` with `status: 'revoked'`.
1173
- */
1174
- revoke(id: string, input?: RevokeSubjectInput, opts?: RequestOptions): Promise<Subject>;
1175
- /**
1176
- * `DELETE /subjects/:id` — deletes the subject's stored photos, face
1177
- * templates and identifiers, and unlinks its sessions. Emits
1178
- * `subject.deleted`. Cannot be undone; a second call answers `404`.
1179
- */
1180
- delete(id: string): Promise<DeletedSubject>;
1181
- }
1182
- /**
1183
- * Sekyro AI partner API client. Server-side only: it carries your API key.
1184
- *
1185
- * Throws {@link SekyroConfigError} synchronously when `apiKey` is missing or
1186
- * `baseUrl` is not `https:` (plain `http:` is accepted for `localhost`,
1187
- * `127.0.0.1` and `[::1]` only).
1188
- *
1189
- * @example
1190
- * ```ts
1191
- * const sekyro = new Sekyro({ apiKey: process.env.SEKYRO_API_KEY! });
1192
- * const { id, clientToken } = await sekyro.sessions.create({ input: { nni: "1357924632" } });
1193
- * ```
1194
- * @public
1195
- */
1196
- declare class Sekyro {
1197
- #private;
1198
- readonly sessions: SessionsApi;
1199
- readonly subjects: SubjectsApi;
1200
- constructor(opts: SekyroOptions);
1201
- /**
1202
- * `GET /flows` — the published flows of the API key's app (default flow
1203
- * first, then by name). Pass a flow's `slug` or `id` as
1204
- * {@link CreateSessionInput.flow}. Needs the `sessions:create` scope; a key
1205
- * not bound to an app answers `401 key_not_bound`.
1206
- */
1207
- listFlows(): Promise<FlowSummary[]>;
1208
- }
1
+ import { a as ServiceId, b as SuppliedIdentityRecord, c as WebhookEnvelope, d as SekyroEnvironment, V as VerificationLevel, e as VerificationAssurance, f as SessionOutcome, g as VerificationUnit, h as SubjectReuseMode, R as ResultRisk, i as ResultCheckProofs, j as ResultDuplicate, F as FetchLike } from './client-DUMp_I-1.js';
2
+ export { k as CameraUnavailableCause, l as CheckState, m as CheckType, n as CreateSessionInput, C as CreatedSession, D as DEFAULT_BASE_URL, o as DEFAULT_TIMEOUT_MS, p as DecisionReason, q as DeepPartial, r as DeletedSubject, s as DeliverOptions, t as DocumentSubstitutionSettings, E as EidResult, u as EligibilityReason, v as FlowOverridableSettings, w as FlowOverrides, x as FlowServiceSettings, y as FlowSummary, I as IdentityMethod, z as LevelTarget, A as ListSessionsQuery, L as Locale, M as MatchReason, P as PolicyFailureReason, B as PortraitReportEntry, G as ProbeBindingReason, H as ProbeQuality, J as ProfileDocumentType, K as RequestOptions, N as ResultTokenResponse, O as RevokeSubjectInput, S as Sekyro, Q as SekyroOptions, T as Session, U as SessionDevice, X as SessionDeviceSignal, Y as SessionEventData, Z as SessionFailureReason, _ as SessionFlowRef, $ as SessionLifecycleEventData, a0 as SessionLinkInput, a1 as SessionNetwork, a2 as SessionNetworkEvent, a3 as SessionPortraitLink, a4 as SessionReviewDecidedEventData, a5 as SessionStatus, a6 as SessionStep, a7 as SessionStepCompletedEventData, a8 as SessionSubjectInput, a9 as SessionsApi, aa as Subject, ab as SubjectAssurance, ac as SubjectDeletedEventData, ad as SubjectEnrolledEventData, ae as SubjectReferenceSource, af as SubjectReuseDocument, ag as SubjectReuseTarget, ah as SubjectSession, ai as SubjectSessionInput, aj as SubjectSessionTarget, ak as SubjectStatus, al as SubjectSuspiciousActivityEventData, am as SubjectUpdatedEventData, an as SubjectsApi, ao as VerifiedContacts, ap as WebhookEnvelopeOf, aq as WebhookEventDataMap, W as WebhookEventType } from './client-DUMp_I-1.js';
1209
3
 
1210
4
  /** Why {@link verifyWebhook} rejected a delivery. @public */
1211
5
  type SekyroWebhookErrorCode = "invalid_signature" | "stale" | "malformed";
@@ -1219,6 +13,29 @@ declare class SekyroWebhookError extends Error {
1219
13
  readonly code: SekyroWebhookErrorCode;
1220
14
  constructor(code: SekyroWebhookErrorCode, message?: string);
1221
15
  }
16
+ /**
17
+ * Why {@link verifyResultToken} rejected a result token:
18
+ * - `invalid_token`: not a JWT, a bad signature, an unknown key, or no `sekyro` claim;
19
+ * - `expired`: past its `exp` (tokens live 10 minutes; fetch a fresh one with
20
+ * {@link SessionsApi.resultToken});
21
+ * - `wrong_audience`: issued for another app;
22
+ * - `wrong_issuer`: not issued by the expected issuer;
23
+ * - `wrong_env`: a sandbox token when you asked for live, or the reverse;
24
+ * - `key_env_mismatch`: the environment claim does not match the key that signed it;
25
+ * - `jwks_unavailable`: the public key set could not be fetched;
26
+ * - `invalid_options`: a missing `appId` or an unsafe `jwksUrl`.
27
+ * @public
28
+ */
29
+ type SekyroResultTokenErrorCode = "invalid_token" | "expired" | "wrong_audience" | "wrong_issuer" | "wrong_env" | "key_env_mismatch" | "jwks_unavailable" | "invalid_options";
30
+ /**
31
+ * Thrown by {@link verifyResultToken}. Do not use the token's content.
32
+ * @public
33
+ */
34
+ declare class SekyroResultTokenError extends Error {
35
+ readonly name = "SekyroResultTokenError";
36
+ readonly code: SekyroResultTokenErrorCode;
37
+ constructor(code: SekyroResultTokenErrorCode, message?: string, cause?: unknown);
38
+ }
1222
39
  /** Why the {@link Sekyro} constructor rejected its options. @public */
1223
40
  type SekyroConfigErrorCode = "missing_api_key" | "invalid_base_url" | "invalid_registry_record";
1224
41
  /**
@@ -1488,4 +305,85 @@ interface VerifyWebhookOptions {
1488
305
  */
1489
306
  declare function verifyWebhook(rawBody: string, header: string | null | undefined, secret: string, opts?: VerifyWebhookOptions): WebhookEnvelope;
1490
307
 
1491
- 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 DocumentSubstitutionSettings, type EidResult, type EligibilityReason, type FetchLike, type FlowErrorDetails, type FlowErrorIssue, type FlowOverridableSettings, type FlowOverrides, type FlowRefErrorDetails, type FlowServiceSettings, type FlowSummary, type FlowWidensErrorDetails, type IdentityMethod, LEGACY_SIGNATURE_HEADER, type Locale, type MatchReason, type PolicyFailureReason, type PortraitReportEntry, type ProbeBindingReason, type ProbeQuality, type ProfileDocumentType, type RegistryFromAnrptsOptions, type RequestOptions, type RevokeSubjectInput, SEKYRO_SIGNATURE_HEADER, Sekyro, SekyroApiError, type SekyroApiErrorCode, type SekyroApiErrorInit, type SekyroClientErrorCode, SekyroConfigError, type SekyroConfigErrorCode, type SekyroEnvironment, type SekyroFlowError, type SekyroFlowErrorCode, type SekyroFlowErrorDetailsMap, type SekyroOptions, SekyroWebhookError, type SekyroWebhookErrorCode, type ServiceId, type Session, type SessionDevice, type SessionDeviceSignal, type SessionEventData, type SessionFailureReason, type SessionFlowRef, 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, isFlowError, registryFromAnrpts, signatureFromHeaders, verifyWebhook };
308
+ /** Issuer of production result tokens. @public */
309
+ declare const DEFAULT_RESULT_TOKEN_ISSUER = "https://api.sekyro.ai";
310
+ /** Path of the public key set under the issuer origin. @public */
311
+ declare const RESULT_TOKEN_JWKS_PATH = "/.well-known/sekyro/jwks.json";
312
+ /** Options of {@link verifyResultToken}. @public */
313
+ interface VerifyResultTokenOptions {
314
+ /**
315
+ * Your app id, or every app id this server accepts. The token's `aud` must
316
+ * be one of them; {@link VerifiedResult.appId} says which.
317
+ */
318
+ appId: string | string[];
319
+ /** Expected `iss`. Default {@link DEFAULT_RESULT_TOKEN_ISSUER}. */
320
+ issuer?: string;
321
+ /** Key set URL. Default `${issuer}/.well-known/sekyro/jwks.json`. Must be `https:` (plain `http:` only on a loopback host). */
322
+ jwksUrl?: string;
323
+ /**
324
+ * The environment you accept. A sandbox token is refused when you ask for
325
+ * `live`, and the reverse. Omit to accept both and read {@link VerifiedResult.env}.
326
+ */
327
+ env?: SekyroEnvironment;
328
+ /** Custom `fetch` for the key set (tests, proxies). Default `globalThis.fetch`. */
329
+ fetch?: FetchLike;
330
+ /** Clock returning epoch milliseconds. Default `Date.now`. */
331
+ now?: () => number;
332
+ /** Allowed clock skew in seconds for `exp` / `iat`. Default 30. */
333
+ clockToleranceSec?: number;
334
+ }
335
+ /**
336
+ * The verified content of a result token, in camelCase.
337
+ * @public
338
+ */
339
+ interface VerifiedResult {
340
+ sessionId: string;
341
+ orgId: string;
342
+ /** The app id the token was issued for (its `aud`), one of {@link VerifyResultTokenOptions.appId}. */
343
+ appId: string;
344
+ /** The subject (person) the session is linked to, when known (`sub`). */
345
+ subjectId?: string;
346
+ /** Your own id for the person, from `subject.externalRef` at create. */
347
+ externalRef?: string;
348
+ env: SekyroEnvironment;
349
+ level: VerificationLevel | null;
350
+ assurance: VerificationAssurance | null;
351
+ outcome: SessionOutcome | null;
352
+ unit: VerificationUnit | null;
353
+ /** ISO 8601: when the session reached its terminal state. */
354
+ verifiedAt: string;
355
+ /** The flow version the session ran. */
356
+ flow?: {
357
+ slug: string;
358
+ version: number;
359
+ };
360
+ /** `recheck` or `step_up` on a session created for an existing subject; absent otherwise. */
361
+ reuseMode?: SubjectReuseMode;
362
+ risk?: ResultRisk;
363
+ checks?: ResultCheckProofs;
364
+ duplicates?: ResultDuplicate[];
365
+ }
366
+ /**
367
+ * Verify a Sekyro AI result token (the `resultToken` of a terminal webhook,
368
+ * or {@link SessionsApi.resultToken}) and return its content.
369
+ *
370
+ * Checks the ES256 signature against the public key set (fetched from
371
+ * `${issuer}/.well-known/sekyro/jwks.json` and cached for 10 minutes; an
372
+ * unknown key id fetches it again), the issuer, the audience (your app id),
373
+ * the expiry, and that the environment claim matches the key that signed it
374
+ * (sandbox keys have ids starting `sbx-`). Throws
375
+ * {@link SekyroResultTokenError} otherwise; never trust the token's content
376
+ * before this resolves.
377
+ *
378
+ * @example
379
+ * ```ts
380
+ * const result = await verifyResultToken(token, { appId: process.env.SEKYRO_APP_ID!, env: "live" });
381
+ * if (result.outcome === "approved" && (result.level ?? 0) >= 1) {
382
+ * // a verified real person
383
+ * }
384
+ * ```
385
+ * @public
386
+ */
387
+ declare function verifyResultToken(token: string, opts: VerifyResultTokenOptions): Promise<VerifiedResult>;
388
+
389
+ export { type AnrptsPersonneResponse, DEFAULT_RESULT_TOKEN_ISSUER, FetchLike, type FlowErrorDetails, type FlowErrorIssue, type FlowRefErrorDetails, type FlowWidensErrorDetails, LEGACY_SIGNATURE_HEADER, RESULT_TOKEN_JWKS_PATH, type RegistryFromAnrptsOptions, ResultCheckProofs, ResultDuplicate, ResultRisk, SEKYRO_SIGNATURE_HEADER, SekyroApiError, type SekyroApiErrorCode, type SekyroApiErrorInit, type SekyroClientErrorCode, SekyroConfigError, type SekyroConfigErrorCode, SekyroEnvironment, type SekyroFlowError, type SekyroFlowErrorCode, type SekyroFlowErrorDetailsMap, SekyroResultTokenError, type SekyroResultTokenErrorCode, SekyroWebhookError, type SekyroWebhookErrorCode, ServiceId, SessionOutcome, SubjectReuseMode, SuppliedIdentityRecord, VerificationAssurance, VerificationLevel, VerificationUnit, type VerifiedResult, type VerifyResultTokenOptions, type VerifyWebhookOptions, WebhookEnvelope, isFlowError, registryFromAnrpts, signatureFromHeaders, verifyResultToken, verifyWebhook };