@sekyro/node 0.1.0-rc.2 → 0.1.0-rc.3

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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Wire-level enums and shapes of the Sekyro partner API (`/api/v2`). These
2
+ * Wire-level enums and shapes of the Sekyro AI partner API (`/api/v2`). These
3
3
  * mirror the backend's frozen partner contract; they are plain string unions so
4
4
  * a newer API value never throws at runtime — it simply widens the type in the
5
5
  * next SDK release.
@@ -14,10 +14,71 @@ type SessionStatus = "created" | "in_progress" | "review_pending" | "completed"
14
14
  * @public
15
15
  */
16
16
  type SessionOutcome = "approved" | "review" | "rejected";
17
+ /**
18
+ * How the person proves their identity on an optional-document app: type the
19
+ * national number, or photograph the document.
20
+ * @public
21
+ */
22
+ type IdentityMethod = "national_number" | "document_photo";
17
23
  /** The verification checks a flow may contain. @public */
18
24
  type CheckType = "liveness" | "face_match" | "doc_ocr" | "population_registry" | "phone_otp";
19
25
  /** Per-check execution state within a session. @public */
20
26
  type CheckState = "pending" | "input_required" | "processing" | "passed" | "failed" | "review" | "skipped";
27
+ /**
28
+ * Why a session was stopped by a profile-v2 policy gate rather than by a
29
+ * failed check:
30
+ * - `location_not_allowed` — the end user's IP location is not allowed for the app;
31
+ * - `anonymizer_blocked` — an anonymizer (VPN / proxy / Tor) was detected while the app blocks them;
32
+ * - `capture_tampered` — the device reported a tampered capture (virtual
33
+ * camera, hooking, emulator…) while the app's `device.tamper` is `block`;
34
+ * - `device_velocity_exceeded` — the device already verified more different
35
+ * people than `device.velocity` allows, with `action: 'block'`;
36
+ * - `device_cooldown` — the device had too many rejected sessions
37
+ * (`cooldown` tiers); {@link Session.retryAfter} says when it may try again.
38
+ * Not billed.
39
+ * @public
40
+ */
41
+ type SessionFailureReason = "location_not_allowed" | "anonymizer_blocked" | "capture_tampered" | "device_velocity_exceeded" | "device_cooldown";
42
+ /**
43
+ * Profile-v2 decision-mode reasons a check's {@link SessionStep.reason} may
44
+ * carry when the app decides automatically (`decisions.mode: 'automatic'`)
45
+ * instead of routing to human review. All are retryable within the step's
46
+ * attempts: `liveness_inconclusive` (liveness middle band),
47
+ * `match_inconclusive` (face match middle band on every reference),
48
+ * `document_authenticity` (doc_ocr authenticity warning — retake the original)
49
+ * and `mobile_required` (liveness captured on a desktop — continue on a phone;
50
+ * not billed). Every inconclusive attempt is billed.
51
+ * @public
52
+ */
53
+ type DecisionReason = "liveness_inconclusive" | "match_inconclusive" | "document_authenticity" | "mobile_required";
54
+ /**
55
+ * Profile-v2 eligibility reasons a check's {@link SessionStep.reason} may carry:
56
+ * `document_type_not_accepted` (doc_ocr; the person may pick another
57
+ * document), `nationality_not_allowed` and `underage` (doc_ocr or
58
+ * population_registry; terminal), and `age_unverifiable` (the app sets a
59
+ * minimum age but the birth date could not be read — doc_ocr: retryable with
60
+ * a clearer photo; population_registry: terminal).
61
+ * @public
62
+ */
63
+ type EligibilityReason = "document_type_not_accepted" | "nationality_not_allowed" | "underage" | "age_unverifiable";
64
+ /**
65
+ * Why a profile-v2 policy ended a session: a session-level
66
+ * {@link SessionFailureReason} (location / anonymizer gate), or the failed
67
+ * check's policy reason — an {@link EligibilityReason} or
68
+ * `document_nni_mismatch` (the document belongs to someone other than the
69
+ * NNI the session was created for).
70
+ * @public
71
+ */
72
+ type PolicyFailureReason = SessionFailureReason | EligibilityReason | "document_nni_mismatch";
73
+ /**
74
+ * Profile-v2 matching reasons a check's {@link SessionStep.reason} may carry
75
+ * when the data you sent in {@link CreateSessionInput.subject} differs from the
76
+ * reference identity: `name_mismatch`, `birth_date_mismatch`,
77
+ * `nationality_mismatch` (doc_ocr or population_registry) and
78
+ * `document_number_mismatch` (doc_ocr). All are final.
79
+ * @public
80
+ */
81
+ type MatchReason = "name_mismatch" | "birth_date_mismatch" | "nationality_mismatch" | "document_number_mismatch";
21
82
  /** Hosted-flow languages. @public */
22
83
  type Locale = "ar" | "fr" | "en";
23
84
  /** The environment a webhook was emitted from. @public */
@@ -34,9 +95,11 @@ interface SessionStep {
34
95
  reason?: string;
35
96
  /** Calibrated model score in [0, 1] for scored checks (liveness, face match). */
36
97
  calibratedScore?: number;
98
+ /** The check failed after using all of its attempts. */
99
+ attemptsExhausted?: boolean;
37
100
  }
38
101
  /**
39
- * Out-of-band delivery of the session's hosted link, sent by Sekyro after the
102
+ * Out-of-band delivery of the session's hosted link, sent by Sekyro AI after the
40
103
  * session is created. Best effort: a failed send never fails the create.
41
104
  * @public
42
105
  */
@@ -46,6 +109,148 @@ interface DeliverOptions {
46
109
  /** E.164 phone number of the recipient, e.g. `+22236123456`. */
47
110
  to: string;
48
111
  }
112
+ /**
113
+ * What you already know about the person (`subject` at session create). A
114
+ * closed object: unknown keys are refused (`400`). Every field is optional;
115
+ * `null` or blank counts as absent. Identity fields are compared with the
116
+ * person's document or registry record when the app's profile turns name
117
+ * matching on; `nni` binds the session to that identity.
118
+ * @public
119
+ */
120
+ interface SessionSubjectInput {
121
+ /** 10-digit Mauritanian NNI, as a string. */
122
+ nni?: string;
123
+ /** E.164 phone number, e.g. `+22236123456`. */
124
+ phone?: string;
125
+ fullName?: string;
126
+ /** ISO 8601 date (`YYYY-MM-DD`). */
127
+ birthDate?: string;
128
+ /** ISO 3166-1 alpha-2, e.g. `MR`. */
129
+ nationality?: string;
130
+ /** Identity-document number (1–40 characters). */
131
+ documentNumber?: string;
132
+ /**
133
+ * Your own id for this person (1–128 characters). Echoed on the session and
134
+ * its webhooks, and searchable on the subjects API.
135
+ */
136
+ externalRef?: string;
137
+ }
138
+ /** A document type the backend reads (kyc-docs-v2 vocabulary). @public */
139
+ type ProfileDocumentType = "carte_identite" | "passport";
140
+ /**
141
+ * The settings of an app's eKYC profile that a session may override — a
142
+ * mirror of the backend profile (schema version 2) without `schemaVersion`,
143
+ * `link` (use {@link CreateSessionInput.ttlMinutes}) and `overrides` (the
144
+ * app's own permission). Country codes are ISO 3166-1 alpha-2 (`MR`).
145
+ * Used through {@link EkycProfileOverride}.
146
+ * @public
147
+ */
148
+ interface EkycProfileSettings {
149
+ /** Phone OTP step: off, or on with where the number comes from. */
150
+ phone: "off" | {
151
+ numberSource: "prefilled" | "user_entered" | "prefilled_or_entered";
152
+ };
153
+ document: {
154
+ mode: "off" | "optional" | "mandatory";
155
+ /** Accepted document types: `'*'` (all) or a list, optionally per issuing country. */
156
+ accepted: {
157
+ default: "*" | ProfileDocumentType[];
158
+ byIssuer?: Record<string, ProfileDocumentType[]>;
159
+ };
160
+ /** Expired documents: refused, allowed, or allowed for `graceDays` (1–365) after expiry. */
161
+ expired: "reject" | "allow" | {
162
+ graceDays: number;
163
+ };
164
+ authenticity: "standard" | "off";
165
+ };
166
+ registry: {
167
+ enabled: boolean;
168
+ sources: Array<"MR.anrpts">;
169
+ /** Which identity is the reference for face match and your data. */
170
+ reference?: "document" | "registry";
171
+ /** What happens when the registry has no record for the NNI. */
172
+ onNoRecord?: "fail" | "use_document";
173
+ };
174
+ liveness: {
175
+ enabled: boolean;
176
+ };
177
+ faceMatch: {
178
+ enabled: boolean;
179
+ };
180
+ /** Comparison of your `subject` data with the reference identity. */
181
+ nameMatch?: {
182
+ level: "strict" | "standard" | "lenient";
183
+ onMismatch: "fail" | "signal";
184
+ };
185
+ /** Per-step attempt limits. */
186
+ attempts?: {
187
+ phone: number;
188
+ document: number;
189
+ registry: number;
190
+ liveness: number;
191
+ faceMatch: number;
192
+ };
193
+ /** Where the end user may be (IP location). */
194
+ locations: {
195
+ mode: "all";
196
+ } | {
197
+ mode: "only" | "all_except";
198
+ countries: string[];
199
+ };
200
+ anonymizers: "allow" | "block";
201
+ /** Accepted nationalities: `'*'` or a list, optionally per IP location. */
202
+ nationalities: {
203
+ default: "*" | string[];
204
+ byLocation?: Record<string, "*" | string[]>;
205
+ };
206
+ /** Minimum age in years (1–120), or `null` for none. */
207
+ minAge: number | null;
208
+ /** Automatic decisions instead of human review (`retryConfidentRejects` lets confident rejects retry). */
209
+ decisions?: {
210
+ mode: "automatic" | "review";
211
+ retryConfidentRejects: boolean;
212
+ };
213
+ /** Device cooldown after repeated rejections (at most 5 tiers; `[]` = off). */
214
+ cooldown?: {
215
+ tiers: Array<{
216
+ rejectedSessions: number;
217
+ windowHours: number;
218
+ waitMinutes: number;
219
+ }>;
220
+ };
221
+ /** Device tamper and velocity actions. */
222
+ device?: {
223
+ tamper: "block" | "signal";
224
+ velocity: {
225
+ maxIdentitiesPerDevice: number;
226
+ windowDays: number;
227
+ action: "signal" | "block";
228
+ };
229
+ };
230
+ }
231
+ /**
232
+ * `T` with every property optional, recursively. Arrays are replaced as a
233
+ * whole, never merged element by element.
234
+ * @public
235
+ */
236
+ type DeepPartial<T> = T extends readonly unknown[] ? T : T extends object ? {
237
+ [K in keyof T]?: DeepPartial<T[K]>;
238
+ } : T;
239
+ /**
240
+ * A per-session override of the app's eKYC profile
241
+ * ({@link CreateSessionInput.profile}): any subset of
242
+ * {@link EkycProfileSettings}, at any depth (every field optional). Objects
243
+ * are merged into the app's profile; arrays and scalar values replace it.
244
+ *
245
+ * The API validates the merged result like a profile save. Unknown keys are
246
+ * rejected server-side (`400`, also for keys this SDK version does not type).
247
+ * An override may only make the profile stricter unless the app allows
248
+ * widening (`overrides.allowWidening`); otherwise a widening change answers
249
+ * `403 override_widens_profile`. The changed paths are reported on
250
+ * {@link Session.overriddenFields}.
251
+ * @public
252
+ */
253
+ type EkycProfileOverride = DeepPartial<EkycProfileSettings>;
49
254
  /**
50
255
  * Input of {@link SessionsApi.create}. There is no recipe field: the API key
51
256
  * belongs to an app, and that app's eKYC profile decides the steps.
@@ -54,6 +259,11 @@ interface DeliverOptions {
54
259
  interface CreateSessionInput {
55
260
  /** Pins the hosted-flow language. Omit to let the person's device decide. */
56
261
  locale?: Locale;
262
+ /**
263
+ * Pre-choose the identity method when the app's document step is optional
264
+ * (the hosted method screen is then skipped). Ignored for other apps.
265
+ */
266
+ identityMethod?: IdentityMethod;
57
267
  /**
58
268
  * Session lifetime in minutes. Must fall within the app profile's link
59
269
  * range, otherwise the API answers `400 ttl_out_of_policy`. Omit to use the
@@ -62,6 +272,19 @@ interface CreateSessionInput {
62
272
  ttlMinutes?: number;
63
273
  /** Also send the hosted link to the person by SMS or WhatsApp. */
64
274
  deliver?: DeliverOptions;
275
+ /**
276
+ * What you already know about the person (preferred over `input` for
277
+ * identity data). A key sent in both `subject` and `input` with different
278
+ * values answers `400 subject_input_conflict`.
279
+ */
280
+ subject?: SessionSubjectInput;
281
+ /**
282
+ * Override the app's eKYC profile for this session only (profile v2): a
283
+ * deep-partial {@link EkycProfileOverride}. Unknown keys are rejected
284
+ * server-side; a change that makes the profile less strict answers
285
+ * `403 override_widens_profile` unless the app allows widening.
286
+ */
287
+ profile?: EkycProfileOverride;
65
288
  /**
66
289
  * Step inputs consumed server-side. The known anchors are typed (all
67
290
  * strings — an `nni` is a 10-digit string, never a number); further keys
@@ -102,6 +325,63 @@ interface CreatedSession {
102
325
  /** ISO 8601 — when the session (and its link) stop accepting input. */
103
326
  expiresAt: string;
104
327
  }
328
+ /**
329
+ * The end user's network at the start of the session (IP-derived, approximate).
330
+ * @public
331
+ */
332
+ interface SessionNetworkEvent {
333
+ /** ISO 8601. */
334
+ at: string;
335
+ /** ISO 3166-1 alpha-2. */
336
+ country?: string;
337
+ region?: string;
338
+ city?: string;
339
+ asn?: number;
340
+ operator?: string;
341
+ connectionType: "mobile" | "hosting" | "unknown";
342
+ anonymizer: {
343
+ vpn: boolean;
344
+ tor: boolean;
345
+ hosting: boolean;
346
+ };
347
+ /** IANA time zone reported by the end user's device. */
348
+ browserTimezone?: string;
349
+ /** True when the device time zone does not belong to the IP country. */
350
+ timezoneMismatch?: boolean;
351
+ }
352
+ /**
353
+ * End-user network context; absent when not recorded.
354
+ * @public
355
+ */
356
+ interface SessionNetwork {
357
+ /** The start event's IP address. Never sent in webhooks. */
358
+ ip?: string;
359
+ /** True when a later call came from a different country than the start. */
360
+ countryChanged: boolean;
361
+ start: SessionNetworkEvent;
362
+ }
363
+ /**
364
+ * Device signals detected on the end user's device.
365
+ * @public
366
+ */
367
+ type SessionDeviceSignal = "webdriver" | "headless" | "emulator" | "rooted" | "jailbroken" | "debugger" | "hooking" | "virtual_camera";
368
+ /**
369
+ * End-user device summary; absent when not recorded.
370
+ * @public
371
+ */
372
+ interface SessionDevice {
373
+ platform: "web" | "android" | "ios";
374
+ sdk?: string;
375
+ os?: string;
376
+ osVersion?: string;
377
+ browser?: string;
378
+ browserVersion?: string;
379
+ deviceClass: "mobile" | "tablet" | "desktop" | "unknown";
380
+ model?: string;
381
+ /** Device ids hashed with a key unique to your organization. */
382
+ deviceKeys: string[];
383
+ signals: SessionDeviceSignal[];
384
+ }
105
385
  /**
106
386
  * The whitelisted session view returned by {@link SessionsApi.get}. Never
107
387
  * contains the client token.
@@ -116,6 +396,37 @@ interface Session {
116
396
  expiresAt: string;
117
397
  /** ISO 8601. */
118
398
  createdAt: string;
399
+ /** End-user network context; absent when not recorded. */
400
+ network?: SessionNetwork;
401
+ /** End-user device; absent when not recorded. */
402
+ device?: SessionDevice;
403
+ /**
404
+ * Set when a profile-v2 policy ended the session: a session-level gate
405
+ * (location, anonymizer) or a failed check's policy reason (eligibility,
406
+ * `document_nni_mismatch`). Absent otherwise.
407
+ */
408
+ failureReason?: PolicyFailureReason;
409
+ /** The subject (person) this session is linked to, once an identifier is known. */
410
+ subjectId?: string;
411
+ /** Your own id for the person, from `subject.externalRef` at create. */
412
+ externalRef?: string;
413
+ /**
414
+ * `true` when an identifier this session read already belongs to a
415
+ * different subject than `subjectId` — the subjects are never merged.
416
+ */
417
+ subjectConflict?: boolean;
418
+ /**
419
+ * ISO 8601 — when the end user's device may verify again; set with
420
+ * `failureReason: 'device_cooldown'`.
421
+ */
422
+ retryAfter?: string;
423
+ /** Dotted profile paths this session's {@link CreateSessionInput.profile} changed (e.g. `decisions.mode`). */
424
+ overriddenFields?: string[];
425
+ /**
426
+ * Risk signals recorded on the session (e.g. `device_velocity`,
427
+ * `timezone_mismatch`) — signals only, never a verdict by themselves.
428
+ */
429
+ riskSignals?: string[];
119
430
  }
120
431
  /**
121
432
  * `data` of the terminal / review-pending session lifecycle events
@@ -133,6 +444,54 @@ interface SessionLifecycleEventData {
133
444
  steps?: Array<Pick<SessionStep, "checkType" | "state">>;
134
445
  /** Machine-readable reason, when the transition has one. */
135
446
  reason?: string;
447
+ /** End-user network context (no IP); on `completed` / `failed` when recorded. */
448
+ network?: Omit<SessionNetwork, "ip">;
449
+ /** End-user device; on `completed` / `failed` when recorded. */
450
+ device?: SessionDevice;
451
+ /**
452
+ * On `session.failed`: set when a profile-v2 policy ended the session — a
453
+ * session-level gate (location, anonymizer) or a failed check's policy
454
+ * reason (eligibility, `document_nni_mismatch`).
455
+ */
456
+ failureReason?: PolicyFailureReason;
457
+ /** The subject (person) the session is linked to, when known. */
458
+ subjectId?: string;
459
+ /** Your own id for the person, from `subject.externalRef` at create. */
460
+ externalRef?: string;
461
+ /**
462
+ * `true` when an identifier this session read already belongs to a
463
+ * different subject than `subjectId` — the subjects are never merged.
464
+ */
465
+ subjectConflict?: boolean;
466
+ /**
467
+ * On `session.failed` with `failureReason: 'device_cooldown'`: ISO time
468
+ * from which the device may start a new verification.
469
+ */
470
+ retryAfter?: string;
471
+ /** Dotted profile paths the session's create-time override changed. */
472
+ overriddenFields?: string[];
473
+ }
474
+ /**
475
+ * `data` of `subject.updated` — a session linked to the subject reached a
476
+ * terminal state. Ids and enums only.
477
+ * @public
478
+ */
479
+ interface SubjectUpdatedEventData {
480
+ subjectId: string;
481
+ sessionId: string;
482
+ status: SessionStatus;
483
+ outcome: SessionOutcome | null;
484
+ }
485
+ /**
486
+ * `data` of `subject.suspicious_activity` — a device already linked to
487
+ * ANOTHER subject was used for a session of this subject. `count` is the
488
+ * subject's running total of such events. Ids only.
489
+ * @public
490
+ */
491
+ interface SubjectSuspiciousActivityEventData {
492
+ subjectId: string;
493
+ sessionId: string;
494
+ count: number;
136
495
  }
137
496
  /**
138
497
  * `data` of `session.step.completed` — the check that just passed.
@@ -142,6 +501,15 @@ interface SessionStepCompletedEventData {
142
501
  sessionId: string;
143
502
  checkType: CheckType;
144
503
  state: CheckState;
504
+ /** The subject (person) the session is linked to, when known. */
505
+ subjectId?: string;
506
+ /** Your own id for the person, from `subject.externalRef` at create. */
507
+ externalRef?: string;
508
+ /**
509
+ * `true` when an identifier this session read already belongs to a
510
+ * different subject than `subjectId` — the subjects are never merged.
511
+ */
512
+ subjectConflict?: boolean;
145
513
  }
146
514
  /**
147
515
  * `data` of `session.review_decided` — a human (or the review-timeout sweep)
@@ -154,6 +522,15 @@ interface SessionReviewDecidedEventData {
154
522
  sessionId: string;
155
523
  outcome: "pass" | "fail";
156
524
  reason: string;
525
+ /** The subject (person) the session is linked to, when known. */
526
+ subjectId?: string;
527
+ /** Your own id for the person, from `subject.externalRef` at create. */
528
+ externalRef?: string;
529
+ /**
530
+ * `true` when an identifier this session read already belongs to a
531
+ * different subject than `subjectId` — the subjects are never merged.
532
+ */
533
+ subjectConflict?: boolean;
157
534
  }
158
535
  /**
159
536
  * Event type → the shape of its `data`. Session events are typed; the
@@ -171,6 +548,8 @@ interface WebhookEventDataMap {
171
548
  "session.expired": SessionLifecycleEventData;
172
549
  "session.failed": SessionLifecycleEventData;
173
550
  "subject.enrolled": Record<string, unknown>;
551
+ "subject.updated": SubjectUpdatedEventData;
552
+ "subject.suspicious_activity": SubjectSuspiciousActivityEventData;
174
553
  "subject.deleted": Record<string, unknown>;
175
554
  "aml.match_flagged": Record<string, unknown>;
176
555
  "payment.received": Record<string, unknown>;
@@ -266,11 +645,18 @@ declare class SessionsApi {
266
645
  * cannot create a second session.
267
646
  */
268
647
  create(input?: CreateSessionInput, opts?: RequestOptions): Promise<CreatedSession>;
648
+ /**
649
+ * `POST /sessions/:id/method` — choose the identity method of an
650
+ * optional-document session on the person's behalf (the hosted method
651
+ * screen is then skipped). `409 method_locked` once the choice can no
652
+ * longer change; `409 session_not_active` on a finished or expired session.
653
+ */
654
+ selectMethod(id: string, method: IdentityMethod): Promise<Session>;
269
655
  /** `GET /sessions/:id` — the whitelisted view (no client token). */
270
656
  get(id: string): Promise<Session>;
271
657
  }
272
658
  /**
273
- * Sekyro partner API client. Server-side only: it carries your API key.
659
+ * Sekyro AI partner API client. Server-side only: it carries your API key.
274
660
  *
275
661
  * Throws {@link SekyroConfigError} synchronously when `apiKey` is missing or
276
662
  * `baseUrl` is not `https:` (plain `http:` is accepted for `localhost`,
@@ -292,7 +678,7 @@ declare class Sekyro {
292
678
  type SekyroWebhookErrorCode = "invalid_signature" | "stale" | "malformed";
293
679
  /**
294
680
  * Thrown by {@link verifyWebhook}. Respond with a 4xx and do not process the
295
- * body; Sekyro retries deliveries that do not receive a 2xx.
681
+ * body; Sekyro AI retries deliveries that do not receive a 2xx.
296
682
  * @public
297
683
  */
298
684
  declare class SekyroWebhookError extends Error {
@@ -352,7 +738,7 @@ declare class SekyroApiError extends Error {
352
738
  */
353
739
  declare const SEKYRO_SIGNATURE_HEADER = "x-sekyro-signature";
354
740
  /**
355
- * The pre-rebrand name of the signature header. Sekyro sends BOTH headers with
741
+ * The pre-rebrand name of the signature header. Sekyro AI sends BOTH headers with
356
742
  * the same value during the transition; prefer {@link SEKYRO_SIGNATURE_HEADER}
357
743
  * and use {@link signatureFromHeaders} to accept either.
358
744
  * @public
@@ -380,13 +766,13 @@ interface VerifyWebhookOptions {
380
766
  now?: () => number;
381
767
  }
382
768
  /**
383
- * Verify a Sekyro webhook delivery and return its parsed envelope, discriminated
769
+ * Verify a Sekyro AI webhook delivery and return its parsed envelope, discriminated
384
770
  * on `event` (narrow with a `switch` to type `data`).
385
771
  *
386
772
  * - `rawBody` MUST be the request body exactly as received (before any JSON
387
773
  * parsing / re-serialisation); the signature covers those bytes.
388
774
  * - Accepts the delivery when ANY `v1` in the header matches, which is what
389
- * makes secret rotation seamless (Sekyro signs with current + previous).
775
+ * makes secret rotation seamless (Sekyro AI signs with current + previous).
390
776
  * - Uses `node:crypto` only; the digest compare is constant-time.
391
777
  *
392
778
  * Checks run in this order: header/secret present → header well-formed →
@@ -401,4 +787,4 @@ interface VerifyWebhookOptions {
401
787
  */
402
788
  declare function verifyWebhook(rawBody: string, header: string | null | undefined, secret: string, opts?: VerifyWebhookOptions): WebhookEnvelope;
403
789
 
404
- export { type CheckState, type CheckType, type CreateSessionInput, type CreatedSession, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type DeliverOptions, type FetchLike, LEGACY_SIGNATURE_HEADER, type Locale, type RequestOptions, SEKYRO_SIGNATURE_HEADER, Sekyro, SekyroApiError, type SekyroApiErrorInit, type SekyroClientErrorCode, SekyroConfigError, type SekyroConfigErrorCode, type SekyroEnvironment, type SekyroOptions, SekyroWebhookError, type SekyroWebhookErrorCode, type Session, type SessionEventData, type SessionLifecycleEventData, type SessionOutcome, type SessionReviewDecidedEventData, type SessionStatus, type SessionStep, type SessionStepCompletedEventData, SessionsApi, type VerifyWebhookOptions, type WebhookEnvelope, type WebhookEnvelopeOf, type WebhookEventDataMap, type WebhookEventType, signatureFromHeaders, verifyWebhook };
790
+ export { type CheckState, type CheckType, type CreateSessionInput, type CreatedSession, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, type DecisionReason, type DeepPartial, type DeliverOptions, type EkycProfileOverride, type EkycProfileSettings, type EligibilityReason, type FetchLike, type IdentityMethod, LEGACY_SIGNATURE_HEADER, type Locale, type MatchReason, type PolicyFailureReason, type ProfileDocumentType, type RequestOptions, 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 SessionReviewDecidedEventData, type SessionStatus, type SessionStep, type SessionStepCompletedEventData, type SessionSubjectInput, SessionsApi, type SubjectSuspiciousActivityEventData, type SubjectUpdatedEventData, type VerifyWebhookOptions, type WebhookEnvelope, type WebhookEnvelopeOf, type WebhookEventDataMap, type WebhookEventType, signatureFromHeaders, verifyWebhook };