@sekyro/node 0.1.0-rc.4 → 0.1.0-rc.6

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.cts CHANGED
@@ -101,6 +101,12 @@ type SekyroEnvironment = "sandbox" | "live";
101
101
  interface SessionStep {
102
102
  checkType: CheckType;
103
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;
104
110
  /** Machine-readable failure/review reason, when the check has one. */
105
111
  reason?: string;
106
112
  /** Calibrated model score in [0, 1] for scored checks (liveness, face match). */
@@ -200,68 +206,98 @@ interface SessionSubjectInput {
200
206
  * again with liveness + face match only, against the reference photo stored
201
207
  * on the subject. Name exactly one target. Errors at create:
202
208
  * `404 subject_not_found`, `409 subject_revoked`, `409 subject_no_reference`,
203
- * `409 subject_stale` (older than the app's `reuse.maxAgeMonths`) and
209
+ * `409 subject_stale` (older than the flow's `returning.maxAgeMonths`),
210
+ * `409 reuse_disabled` (the flow turns returning-user sessions off) and
204
211
  * `400` / `409 reuse_target_ambiguous`.
205
212
  */
206
213
  reuse?: SubjectReuseTarget;
207
214
  }
215
+ /**
216
+ * An identity document for {@link SubjectReuseTarget}: the document type, the
217
+ * issuing country (ISO 3166-1 alpha-2 or alpha-3) and its number.
218
+ * @public
219
+ */
220
+ interface SubjectReuseDocument {
221
+ type: "national_id" | "passport" | "residence_permit";
222
+ issuingCountry: string;
223
+ number: string;
224
+ }
208
225
  /**
209
226
  * 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.
227
+ * subject id, your own `externalRef` (must match a single subject), the NNI
228
+ * or an identity document the person verified with.
211
229
  * @public
212
230
  */
213
231
  type SubjectReuseTarget = {
214
232
  subjectId: string;
215
233
  externalRef?: never;
216
234
  nni?: never;
235
+ document?: never;
217
236
  } | {
218
237
  externalRef: string;
219
238
  subjectId?: never;
220
239
  nni?: never;
240
+ document?: never;
221
241
  } | {
222
242
  nni: string;
223
243
  subjectId?: never;
224
244
  externalRef?: never;
245
+ document?: never;
246
+ } | {
247
+ document: SubjectReuseDocument;
248
+ subjectId?: never;
249
+ externalRef?: never;
250
+ nni?: never;
225
251
  };
226
252
  /** A document type the backend reads (kyc-docs-v2 vocabulary; `carte_identite` = identity card of any issuer). @public */
227
253
  type ProfileDocumentType = "carte_identite" | "passport" | "residence_permit";
228
254
  /**
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}.
255
+ * The services a Sekyro flow is built from. A session's steps
256
+ * ({@link SessionStep.service}) and {@link FlowOverrides.services} use these
257
+ * ids; one service may run several checks (e.g. `liveness` with an ICAO
258
+ * portrait).
234
259
  * @public
235
260
  */
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[];
261
+ 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";
262
+ /**
263
+ * The general settings of a flow that a session may override (the flow's
264
+ * `link`, `language` and override policy are not overridable). Country codes
265
+ * are ISO 3166-1 alpha-2 (`MR`). Used through {@link FlowOverrides.settings}.
266
+ * @public
267
+ */
268
+ interface FlowOverridableSettings {
269
+ /** Who may be verified. */
270
+ eligibility: {
271
+ /** Accepted nationalities: `'*'` or a list, optionally per IP location. */
272
+ nationalities: {
273
+ default: "*" | string[];
274
+ byLocation?: Record<string, "*" | string[]>;
259
275
  };
276
+ /** Minimum age in years (1–120), or `null` for none. */
277
+ minAge: number | null;
260
278
  };
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";
279
+ /** Automatic decisions instead of human review (`retryConfidentRejects` lets confident rejects retry). */
280
+ decisions?: {
281
+ mode: "automatic" | "review";
282
+ retryConfidentRejects: boolean;
283
+ };
284
+ /** Data consistency across sources. */
285
+ fraud: {
286
+ /** Comparison of your `subject` data with the reference identity. */
287
+ nameMatch?: {
288
+ level: "strict" | "standard" | "lenient";
289
+ onMismatch: "fail" | "signal";
290
+ };
291
+ };
292
+ /** Returning-user sessions (`subject.reuse`). */
293
+ returning: {
294
+ /** `false`: returning-user sessions are refused (`409 reuse_disabled`). */
295
+ enabled: boolean;
296
+ /** Maximum age in months (1–120) of the subject's last verification; `null` = no limit. */
297
+ maxAgeMonths: number | null;
298
+ };
299
+ /** Identity-document rules. */
300
+ documents: {
265
301
  /** Accepted document types: `'*'` (all) or a list, optionally per issuing country. */
266
302
  accepted: {
267
303
  default: "*" | ProfileDocumentType[];
@@ -273,98 +309,123 @@ interface EkycProfileSettings {
273
309
  };
274
310
  authenticity: "standard" | "off";
275
311
  };
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";
312
+ }
313
+ /**
314
+ * Document substitution: the portrait a `document` or `nfc` step produces is
315
+ * compared with the registry portrait (or the chip with the printed photo).
316
+ * `signal` records a risk signal on a clear mismatch, `review` also sends the
317
+ * session to review, `off` compares nothing. `failBelow` is the mismatch bound
318
+ * in (0, 1), absent = the platform bound.
319
+ * @public
320
+ */
321
+ interface DocumentSubstitutionSettings {
322
+ mode: "off" | "signal" | "review";
323
+ failBelow?: number;
324
+ }
325
+ /**
326
+ * The per-service settings a session may override, by {@link ServiceId}.
327
+ * `attempts` limits are per step (absent = the platform default).
328
+ * @public
329
+ */
330
+ interface FlowServiceSettings {
331
+ /** Device & Network Intelligence: IP location, anonymizers, cooldown, device and capture postures. */
332
+ device_network: {
333
+ /** Where the end user may be (IP location). */
334
+ locations: {
335
+ mode: "all";
336
+ } | {
337
+ mode: "only" | "all_except";
338
+ countries: string[];
339
+ };
340
+ /** VPN / proxy / Tor. */
341
+ anonymizers: "allow" | "block";
342
+ /** Device cooldown after repeated rejections (at most 5 tiers; `[]` = off). */
343
+ cooldown?: {
344
+ tiers: Array<{
345
+ rejectedSessions: number;
346
+ windowHours: number;
347
+ waitMinutes: number;
348
+ }>;
349
+ };
350
+ /** Device tamper and velocity actions. */
351
+ device?: {
352
+ tamper: "block" | "signal";
353
+ velocity: {
354
+ maxIdentitiesPerDevice: number;
355
+ windowDays: number;
356
+ action: "signal" | "block";
357
+ };
358
+ };
359
+ attestation?: "off" | "signal" | "enforce";
360
+ captureIntegrity?: "off" | "signal" | "enforce";
283
361
  };
284
- liveness: {
285
- enabled: boolean;
362
+ phone_otp: {
363
+ /** Where the number comes from: your `subject.phone` / `input.phone`, the person, or either. */
364
+ numberSource: "prefilled" | "user_entered" | "prefilled_or_entered";
365
+ /** Narrow the channels the person may choose from (absent = every channel the org has on). */
366
+ channels?: Array<"whatsapp" | "sms">;
367
+ /** Which countries codes may be sent to (absent = the flow's allowed `locations`). */
368
+ destinations?: "locations" | "any" | {
369
+ countries: string[];
370
+ };
371
+ attempts?: number;
286
372
  };
287
- faceMatch: {
288
- enabled: boolean;
373
+ email_otp: {
374
+ /** Where the address comes from: your `input.email`, the person, or either. */
375
+ emailSource: "prefilled" | "user_entered" | "prefilled_or_entered";
376
+ /** Disposable (temporary) addresses: allowed, flagged as a risk signal (default), or refused. */
377
+ disposable?: "allow" | "signal" | "block";
378
+ attempts?: number;
289
379
  };
290
- /** Comparison of your `subject` data with the reference identity. */
291
- nameMatch?: {
292
- level: "strict" | "standard" | "lenient";
293
- onMismatch: "fail" | "signal";
380
+ national_eid: {
381
+ /**
382
+ * `'org'` (every national eID the org can use, resolved at session create)
383
+ * or provider ids (`{country}.{provider}`, e.g. `MR.houwiyeti`).
384
+ */
385
+ providers: "org" | string[];
386
+ /** The eID portrait is the face-match reference. */
387
+ portraitReference: boolean;
294
388
  };
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;
389
+ document: {
390
+ attempts?: number;
391
+ /** Compare the document photo with the registry photo (absent = the platform default). */
392
+ documentSubstitution?: DocumentSubstitutionSettings;
304
393
  };
305
- /** Where the end user may be (IP location). */
306
- locations: {
307
- mode: "all";
308
- } | {
309
- mode: "only" | "all_except";
310
- countries: string[];
394
+ /** The chip read (its MRZ source is derived from the flow and is not overridable). */
395
+ nfc: {
396
+ maxAttempts?: number;
397
+ /** Compare the chip photo with the registry photo (absent = the platform default). */
398
+ documentSubstitution?: DocumentSubstitutionSettings;
311
399
  };
312
- anonymizers: "allow" | "block";
313
- /** Accepted nationalities: `'*'` or a list, optionally per IP location. */
314
- nationalities: {
315
- default: "*" | string[];
316
- byLocation?: Record<string, "*" | string[]>;
400
+ population_registry: {
401
+ /** `'org'` (every registry the org can use, resolved at session create) or registry ids. */
402
+ sources: "org" | Array<"MR.anrpts">;
403
+ nniSource: "auto" | "typed" | "bound_only";
404
+ onUncovered: "skip";
405
+ attempts?: number;
317
406
  };
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;
407
+ supplied_identity: Record<string, never>;
408
+ summary_confirm: {
409
+ /** Offer « Reprendre » (re-open the identity source). */
410
+ retake: boolean;
324
411
  };
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
- }>;
412
+ liveness: {
413
+ /** `off`, selfie ICAO `alert` / `reject`, or `id_document` (ID-photo portrait, always rejects). */
414
+ icao: "off" | "alert" | "reject" | "id_document";
415
+ attempts?: number;
332
416
  };
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
- };
417
+ face_match: {
418
+ reference: "auto" | "document" | "registry";
419
+ attempts?: number;
341
420
  };
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
- };
421
+ aml_screening: {
422
+ lists: Array<"sanctions" | "pep" | "adverse_media">;
423
+ onHit: "review" | "fail";
424
+ };
425
+ face_dedup: {
426
+ scope: "app" | "org";
427
+ onDuplicate: "review" | "fail";
356
428
  };
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
429
  }
369
430
  /**
370
431
  * `T` with every property optional, recursively. Arrays are replaced as a
@@ -375,37 +436,86 @@ type DeepPartial<T> = T extends readonly unknown[] ? T : T extends object ? {
375
436
  [K in keyof T]?: DeepPartial<T[K]>;
376
437
  } : T;
377
438
  /**
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.
439
+ * A per-session override of the flow ({@link CreateSessionInput.overrides}).
440
+ *
441
+ * - `settings`: any subset of {@link FlowOverridableSettings}, at any depth.
442
+ * - `services`: keyed by service (applied to every node of that service): any
443
+ * subset of its {@link FlowServiceSettings}, or `'off'` to remove it from
444
+ * the flow. A service the flow does not use is ignored.
445
+ *
446
+ * Objects merge into the flow; arrays and scalar values replace. Unknown keys
447
+ * and invalid results answer `400 override_invalid`. An override may only
448
+ * make the flow stricter unless the flow allows widening; turning a service
449
+ * `'off'` always widens. A widening change answers `403 override_widens_flow`.
450
+ * The changed paths are reported on {@link Session.overriddenFields}.
382
451
  *
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}.
452
+ * @example
453
+ * ```ts
454
+ * await sekyro.sessions.create({
455
+ * flow: "foreigners",
456
+ * overrides: {
457
+ * settings: { eligibility: { minAge: 21 } },
458
+ * services: { liveness: { attempts: 2 } },
459
+ * },
460
+ * });
461
+ * ```
462
+ * @public
463
+ */
464
+ interface FlowOverrides {
465
+ settings?: DeepPartial<FlowOverridableSettings>;
466
+ services?: {
467
+ [S in ServiceId]?: DeepPartial<FlowServiceSettings[S]> | "off";
468
+ };
469
+ }
470
+ /**
471
+ * The flow version a session runs: its `flw_` id, its slug and the published
472
+ * version pinned at create.
389
473
  * @public
390
474
  */
391
- type EkycProfileOverride = DeepPartial<EkycProfileSettings>;
475
+ interface SessionFlowRef {
476
+ id: string;
477
+ slug: string;
478
+ version: number;
479
+ }
392
480
  /**
393
- * Input of {@link SessionsApi.create}. There is no recipe field: the API key
394
- * belongs to an app, and that app's eKYC profile decides the steps.
481
+ * One flow of your app, from {@link Sekyro.listFlows}. Published flows only;
482
+ * pass `slug` or `id` as {@link CreateSessionInput.flow}.
483
+ * @public
484
+ */
485
+ interface FlowSummary {
486
+ /** `flw_` id. */
487
+ id: string;
488
+ slug: string;
489
+ name: string;
490
+ /** The published version a new session runs. */
491
+ version: number;
492
+ /** The flow a session runs when you send no `flow`. */
493
+ isDefault: boolean;
494
+ }
495
+ /**
496
+ * Input of {@link SessionsApi.create}. The API key belongs to an app; the
497
+ * app's flow (its default flow, or the one named in `flow`) decides the steps.
395
498
  * @public
396
499
  */
397
500
  interface CreateSessionInput {
501
+ /**
502
+ * The app's flow to run: its slug (e.g. `foreigners`) or `flw_` id, as
503
+ * {@link Sekyro.listFlows} lists them. Absent = the app's default flow.
504
+ * Errors: `404 flow_not_found` (no such flow in the key's app),
505
+ * `409 flow_not_published`, `409 flow_archived`.
506
+ */
507
+ flow?: string;
398
508
  /** Pins the hosted-flow language. Omit to let the person's device decide. */
399
509
  locale?: Locale;
400
510
  /**
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.
511
+ * Pre-choose the identity method when the flow offers a choice of identity
512
+ * method (the hosted choice screen is then skipped). Ignored otherwise.
403
513
  */
404
514
  identityMethod?: IdentityMethod;
405
515
  /**
406
- * Session lifetime in minutes. Must fall within the app profile's link
407
- * range, otherwise the API answers `400 ttl_out_of_policy`. Omit to use the
408
- * profile's default.
516
+ * Session lifetime in minutes. Must fall within the flow's link range,
517
+ * otherwise the API answers `400 ttl_out_of_policy`. Omit to use the
518
+ * flow's default.
409
519
  */
410
520
  ttlMinutes?: number;
411
521
  /** Also send the hosted link to the person by SMS or WhatsApp. */
@@ -417,12 +527,21 @@ interface CreateSessionInput {
417
527
  */
418
528
  subject?: SessionSubjectInput;
419
529
  /**
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.
530
+ * Your own id for the agent filming the person, for flows whose liveness
531
+ * capture is `assisted` (1 to 128 characters). Stored as is and echoed on
532
+ * the session and in webhooks.
424
533
  */
425
- profile?: EkycProfileOverride;
534
+ assistedBy?: {
535
+ externalRef: string;
536
+ };
537
+ /**
538
+ * Override the flow for this session only ({@link FlowOverrides}): its
539
+ * general settings and per-service settings, or turn a service `'off'`.
540
+ * `400 override_invalid` for an unknown key or an invalid result;
541
+ * `403 override_widens_flow` for a change that makes the flow less strict
542
+ * when the flow does not allow widening.
543
+ */
544
+ overrides?: FlowOverrides;
426
545
  /**
427
546
  * Step inputs consumed server-side. The known anchors are typed (all
428
547
  * strings — an `nni` is a 10-digit string, never a number); further keys
@@ -507,6 +626,9 @@ interface CreatedSession {
507
626
  clientToken: string;
508
627
  /** Hosted verification link for this session. */
509
628
  hostedUrl: string;
629
+ /** The flow version the session runs (the default flow when you sent no `flow`). */
630
+ flow?: SessionFlowRef;
631
+ /** The checks on the session's path (checks of a lane not taken are omitted). */
510
632
  steps: SessionStep[];
511
633
  /** ISO 8601 — when the session (and its link) stop accepting input. */
512
634
  expiresAt: string;
@@ -612,13 +734,18 @@ interface VerifiedContacts {
612
734
  */
613
735
  interface Session {
614
736
  id: string;
737
+ /** Sekyro flows: the flow version the session runs (absent on sessions created before flows). */
738
+ flow?: SessionFlowRef;
615
739
  status: SessionStatus;
616
740
  outcome: SessionOutcome | null;
741
+ /** The checks on the session's path (checks of a lane the person did not take are omitted). */
617
742
  steps: SessionStep[];
618
743
  /** ISO 8601. */
619
744
  expiresAt: string;
620
745
  /** ISO 8601. */
621
746
  createdAt: string;
747
+ /** ISO 8601: when the session reached a terminal state; `null` while it is active. */
748
+ completedAt?: string | null;
622
749
  /** End-user network context; absent when not recorded. */
623
750
  network?: SessionNetwork;
624
751
  /** End-user device; absent when not recorded. */
@@ -633,6 +760,10 @@ interface Session {
633
760
  subjectId?: string;
634
761
  /** Your own id for the person, from `subject.externalRef` at create. */
635
762
  externalRef?: string;
763
+ /** The agent reference sent as `assistedBy` at create, for assisted capture. */
764
+ assistedBy?: {
765
+ externalRef: string;
766
+ };
636
767
  /**
637
768
  * `true` when an identifier this session read already belongs to a
638
769
  * different subject than `subjectId` — the subjects are never merged.
@@ -643,7 +774,10 @@ interface Session {
643
774
  * `failureReason: 'device_cooldown'`.
644
775
  */
645
776
  retryAfter?: string;
646
- /** Dotted profile paths this session's {@link CreateSessionInput.profile} changed (e.g. `decisions.mode`). */
777
+ /**
778
+ * Dotted paths this session's {@link CreateSessionInput.overrides} changed
779
+ * (e.g. `settings.decisions.mode`, `services.liveness`).
780
+ */
647
781
  overriddenFields?: string[];
648
782
  /**
649
783
  * Risk signals recorded on the session (e.g. `device_velocity`,
@@ -675,14 +809,17 @@ interface Session {
675
809
  * `session.expired` | `session.rejected`) — ids and enums only, no civil data,
676
810
  * scores or links. `outcome` and `steps` travel on `completed` / `failed`;
677
811
  * `review_pending` carries `steps` without an outcome; `expired` carries the
678
- * status alone.
812
+ * status and the session's refs (`flow`, subject ids, `overriddenFields`).
679
813
  * @public
680
814
  */
681
815
  interface SessionLifecycleEventData {
682
816
  sessionId: string;
683
817
  status: SessionStatus;
684
818
  outcome?: SessionOutcome | null;
685
- steps?: Array<Pick<SessionStep, "checkType" | "state">>;
819
+ /** The checks on the session's path (checks of a lane the person did not take are omitted). */
820
+ steps?: Array<Pick<SessionStep, "checkType" | "state" | "nodeId" | "service">>;
821
+ /** Sekyro flows: the flow version the session runs (absent on sessions created before flows). */
822
+ flow?: SessionFlowRef;
686
823
  /** Machine-readable reason, when the transition has one. */
687
824
  reason?: string;
688
825
  /** End-user network context (no IP); on `completed` / `failed` when recorded. */
@@ -713,7 +850,7 @@ interface SessionLifecycleEventData {
713
850
  * from which the device may start a new verification.
714
851
  */
715
852
  retryAfter?: string;
716
- /** Dotted profile paths the session's create-time override changed. */
853
+ /** Dotted paths the session's create-time {@link CreateSessionInput.overrides} changed. */
717
854
  overriddenFields?: string[];
718
855
  /** On `completed` / `failed`: the contacts verified in this session (passed OTP checks only). */
719
856
  verifiedContacts?: VerifiedContacts;
@@ -818,8 +955,8 @@ interface Subject {
818
955
  /**
819
956
  * A returning-user session can be created now: active, a reference photo is
820
957
  * stored, and the last FULL verification (returning-user sessions do not
821
- * reset it) is within your app's
822
- * `reuse.maxAgeMonths` (when set).
958
+ * reset it) is within the returning-user limit of your app's default flow
959
+ * (`settings.returning`: turned on, and `maxAgeMonths` when set).
823
960
  */
824
961
  returningReady: boolean;
825
962
  /** The verified name / birth date of the latest approved session. */
@@ -857,6 +994,12 @@ interface SessionStepCompletedEventData {
857
994
  sessionId: string;
858
995
  checkType: CheckType;
859
996
  state: CheckState;
997
+ /** Sekyro flows: the flow node of the passed check. */
998
+ nodeId?: string;
999
+ /** Sekyro flows: the service of that node. */
1000
+ service?: ServiceId;
1001
+ /** Sekyro flows: the flow version the session runs (absent on sessions created before flows). */
1002
+ flow?: SessionFlowRef;
860
1003
  /** The subject (person) the session is linked to, when known. */
861
1004
  subjectId?: string;
862
1005
  /** Your own id for the person, from `subject.externalRef` at create. */
@@ -884,6 +1027,8 @@ interface SessionReviewDecidedEventData {
884
1027
  sessionId: string;
885
1028
  outcome: "pass" | "fail";
886
1029
  reason: string;
1030
+ /** Sekyro flows: the flow version the session runs (absent on sessions created before flows). */
1031
+ flow?: SessionFlowRef;
887
1032
  /** The subject (person) the session is linked to, when known. */
888
1033
  subjectId?: string;
889
1034
  /** Your own id for the person, from `subject.externalRef` at create. */
@@ -1018,6 +1163,9 @@ declare class SessionsApi {
1018
1163
  * optional-document session on the person's behalf (the hosted method
1019
1164
  * screen is then skipped). `409 method_locked` once the choice can no
1020
1165
  * longer change; `409 session_not_active` on a finished or expired session.
1166
+ * On a flow session: `409 choice_not_found` (the flow has no identity-method
1167
+ * choice), `409 choice_locked`, `409 choice_option_unavailable` (see
1168
+ * {@link isFlowError}).
1021
1169
  */
1022
1170
  selectMethod(id: string, method: IdentityMethod): Promise<Session>;
1023
1171
  /** `GET /sessions/:id` — the whitelisted view (no client token). */
@@ -1079,9 +1227,17 @@ declare class SubjectsApi {
1079
1227
  * @public
1080
1228
  */
1081
1229
  declare class Sekyro {
1230
+ #private;
1082
1231
  readonly sessions: SessionsApi;
1083
1232
  readonly subjects: SubjectsApi;
1084
1233
  constructor(opts: SekyroOptions);
1234
+ /**
1235
+ * `GET /flows` — the published flows of the API key's app (default flow
1236
+ * first, then by name). Pass a flow's `slug` or `id` as
1237
+ * {@link CreateSessionInput.flow}. Needs the `sessions:create` scope; a key
1238
+ * not bound to an app answers `401 key_not_bound`.
1239
+ */
1240
+ listFlows(): Promise<FlowSummary[]>;
1085
1241
  }
1086
1242
 
1087
1243
  /** Why {@link verifyWebhook} rejected a delivery. @public */
@@ -1109,15 +1265,104 @@ declare class SekyroConfigError extends TypeError {
1109
1265
  readonly code: SekyroConfigErrorCode;
1110
1266
  constructor(code: SekyroConfigErrorCode, message?: string);
1111
1267
  }
1268
+ /**
1269
+ * The flow codes of the partner API (Sekyro flows). Each carries
1270
+ * {@link SekyroApiError.details} (see {@link SekyroFlowErrorDetailsMap} and
1271
+ * {@link isFlowError}). On `sessions.create`:
1272
+ * - `flow_not_found` (404): no flow with this slug or `flw_` id in the key's app;
1273
+ * - `flow_not_published` (409): the flow has no published version yet;
1274
+ * - `flow_archived` (409): the flow is archived;
1275
+ * - `override_invalid` (400): an unknown key or value in `overrides`, or a
1276
+ * result that is not a valid flow;
1277
+ * - `override_widens_flow` (403): the override makes the flow less strict
1278
+ * (e.g. a service turned `'off'`) and the flow does not allow widening;
1279
+ * - `reuse_disabled` (409): `subject.reuse` on a flow whose returning-user
1280
+ * sessions are turned off.
1281
+ *
1282
+ * On `sessions.selectMethod` (a flow session's identity-method choice):
1283
+ * `choice_not_found` (409: the session's flow has no identity-method choice),
1284
+ * `choice_locked` (409: it can no longer change) and
1285
+ * `choice_option_unavailable` (409: that method is not offered).
1286
+ * @public
1287
+ */
1288
+ type SekyroFlowErrorCode = "flow_not_found" | "flow_not_published" | "flow_archived" | "override_invalid" | "override_widens_flow" | "reuse_disabled" | "choice_not_found" | "choice_locked" | "choice_option_unavailable";
1289
+ /**
1290
+ * One machine-readable issue of a flow error. Never carries a value you sent.
1291
+ * @public
1292
+ */
1293
+ interface FlowErrorIssue {
1294
+ /**
1295
+ * A flow validation code (e.g. `face_match_needs_liveness`), `schema_invalid`,
1296
+ * `schema_unknown_field`, `field_widens_flow`, or the error's own code.
1297
+ */
1298
+ code: string;
1299
+ /** A short plain English sentence. */
1300
+ message: string;
1301
+ /** The field of your request body, e.g. `overrides.services.liveness.attempts`, `flow`, `subject.reuse`. */
1302
+ path?: string;
1303
+ /** A node of your app's flow. */
1304
+ nodeId?: string;
1305
+ /** The service of that node (or of the `overrides.services` key). */
1306
+ service?: ServiceId;
1307
+ /** `override_widens_flow`: the widened field, e.g. `settings.eligibility.minAge`, `services.nfc`. */
1308
+ field?: string;
1309
+ }
1310
+ /** `details` common to every flow error: at least one issue. @public */
1311
+ interface FlowErrorDetails {
1312
+ issues: FlowErrorIssue[];
1313
+ }
1314
+ /** `details` of `override_widens_flow`: the widened fields, plus one `field_widens_flow` issue each. @public */
1315
+ interface FlowWidensErrorDetails extends FlowErrorDetails {
1316
+ fields: string[];
1317
+ }
1318
+ /** `details` of `flow_not_published`, `flow_archived` and `reuse_disabled`: the flow concerned. @public */
1319
+ interface FlowRefErrorDetails extends FlowErrorDetails {
1320
+ flow: {
1321
+ id: string;
1322
+ slug: string;
1323
+ };
1324
+ }
1325
+ /**
1326
+ * {@link SekyroFlowErrorCode} → the shape of its {@link SekyroApiError.details}.
1327
+ * @public
1328
+ */
1329
+ interface SekyroFlowErrorDetailsMap {
1330
+ flow_not_found: FlowErrorDetails;
1331
+ flow_not_published: FlowRefErrorDetails;
1332
+ flow_archived: FlowRefErrorDetails;
1333
+ override_invalid: FlowErrorDetails;
1334
+ override_widens_flow: FlowWidensErrorDetails;
1335
+ reuse_disabled: FlowRefErrorDetails;
1336
+ choice_not_found: FlowErrorDetails;
1337
+ choice_locked: FlowErrorDetails;
1338
+ choice_option_unavailable: FlowErrorDetails;
1339
+ }
1340
+ /**
1341
+ * A {@link SekyroApiError} narrowed by {@link isFlowError}: `code` is a flow
1342
+ * code and `details` has that code's shape.
1343
+ * @public
1344
+ */
1345
+ type SekyroFlowError<C extends SekyroFlowErrorCode = SekyroFlowErrorCode> = C extends SekyroFlowErrorCode ? SekyroApiError & {
1346
+ readonly code: C;
1347
+ readonly details: SekyroFlowErrorDetailsMap[C];
1348
+ } : never;
1349
+ /**
1350
+ * {@link SekyroApiError.code}: an SDK code, a documented flow code, or any
1351
+ * other API code (the union stays open so a new API code never breaks a build).
1352
+ * @public
1353
+ */
1354
+ type SekyroApiErrorCode = SekyroClientErrorCode | SekyroFlowErrorCode | (string & {});
1112
1355
  /** Constructor input of {@link SekyroApiError}. @public */
1113
1356
  interface SekyroApiErrorInit {
1114
1357
  /** HTTP status; `0` when no response was received (network / timeout). */
1115
1358
  status: number;
1116
1359
  /** API error code, or `network` | `timeout` | `http_error` | `malformed_response`. */
1117
- code: string;
1360
+ code: SekyroApiErrorCode;
1118
1361
  message: string;
1119
1362
  /** The API's `request_id`, when the response carried one. */
1120
1363
  requestId?: string;
1364
+ /** The API's `error.details`, when the response carried them (the flow codes: {@link SekyroFlowErrorDetailsMap}). */
1365
+ details?: unknown;
1121
1366
  /** Underlying error (network failures, abort reasons, JSON parse errors). */
1122
1367
  cause?: unknown;
1123
1368
  }
@@ -1135,11 +1380,35 @@ type SekyroClientErrorCode = "network" | "timeout" | "http_error" | "malformed_r
1135
1380
  declare class SekyroApiError extends Error {
1136
1381
  readonly name = "SekyroApiError";
1137
1382
  readonly status: number;
1138
- readonly code: string;
1383
+ readonly code: SekyroApiErrorCode;
1139
1384
  readonly requestId: string | undefined;
1385
+ /**
1386
+ * The API's `error.details`, when the response carried them; `undefined`
1387
+ * otherwise. Untyped here: narrow with {@link isFlowError} to read a flow
1388
+ * code's documented shape ({@link SekyroFlowErrorDetailsMap}).
1389
+ */
1390
+ readonly details: unknown;
1140
1391
  readonly retryable: boolean;
1141
1392
  constructor(init: SekyroApiErrorInit);
1142
1393
  }
1394
+ /**
1395
+ * Narrow an error to a flow error, optionally to given codes: `true` only for
1396
+ * a {@link SekyroApiError} with a flow code whose `details` have that code's
1397
+ * documented shape, so `err.details` is typed after the check. An error whose
1398
+ * `details` are missing or unexpected is not narrowed (read `err.code`).
1399
+ *
1400
+ * @example
1401
+ * ```ts
1402
+ * try {
1403
+ * await sekyro.sessions.create({ flow, overrides });
1404
+ * } catch (err) {
1405
+ * if (isFlowError(err, "override_widens_flow")) err.details.fields; // string[]
1406
+ * else if (isFlowError(err)) err.details.issues.map((i) => i.path);
1407
+ * }
1408
+ * ```
1409
+ * @public
1410
+ */
1411
+ declare function isFlowError<C extends SekyroFlowErrorCode = SekyroFlowErrorCode>(err: unknown, ...codes: C[]): err is SekyroFlowError<C>;
1143
1412
 
1144
1413
  /**
1145
1414
  * The raw ANRPTS `getPersonne` answer your server received with your own
@@ -1252,4 +1521,4 @@ interface VerifyWebhookOptions {
1252
1521
  */
1253
1522
  declare function verifyWebhook(rawBody: string, header: string | null | undefined, secret: string, opts?: VerifyWebhookOptions): WebhookEnvelope;
1254
1523
 
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 };
1524
+ 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 SubjectReuseDocument, 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 };