@kyciris/core 0.1.2 → 1.0.0

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,210 +1,1452 @@
1
1
  /**
2
- * Credentials required to initialize the KYC SDK
2
+ * The largest upload the gateway accepts (MAX_IMAGE_FILE_SIZE). Checked here
3
+ * too, when the SDK can see the bytes, so an 8MB upload fails instantly
4
+ * instead of after the whole body has gone over the wire.
3
5
  */
4
- interface KYCCredentials {
5
- /** API key for authentication */
6
- apiKey: string;
7
- /** Base URL of the KYC API */
8
- baseUrl: string;
6
+ declare const MAX_IMAGE_FILE_SIZE: number;
7
+ /** The only image types the gateway accepts, decided by magic bytes. */
8
+ type ImageMimeType = 'image/jpeg' | 'image/png';
9
+ /**
10
+ * An image to upload, in whichever form the host platform produces.
11
+ *
12
+ * - `Blob` / `File` — browsers, and Node 18+ (`new Blob([bytes])`).
13
+ * - `{ uri }` — React Native: a `file://`, `content://`, `ph://` or `data:` URI
14
+ * from the camera or image picker.
15
+ * - `{ data }` — raw bytes, e.g. `fs.readFileSync()` in Node.
16
+ * - `string` — a `data:` URI, a bare base64 payload, or a React Native file URI.
17
+ */
18
+ type FileInput = Blob | {
19
+ uri: string;
20
+ name?: string;
21
+ type?: string;
22
+ } | {
23
+ data: ArrayBuffer | ArrayBufferView;
24
+ name?: string;
25
+ type?: string;
26
+ } | string | NormalizedFile;
27
+ /** A file ready to be appended to FormData on this platform. */
28
+ interface NormalizedFile {
29
+ /** Blob on web/Node, `{ uri, name, type }` on React Native. */
30
+ value: unknown;
31
+ filename: string;
32
+ /** Sniffed from the bytes where possible, otherwise the caller's hint. */
33
+ mimeType: string;
9
34
  }
35
+ /** True when `value` has already been through {@link normalizeFile}. */
36
+ declare function isNormalizedFile(value: unknown): value is NormalizedFile;
10
37
  /**
11
- * Parameters required to start a verification session
38
+ * React Native's FormData takes `{ uri, name, type }` and streams the file
39
+ * itself; it has no filesystem access from JS to turn that URI into a Blob.
40
+ * Everywhere else, Blob is the portable choice.
12
41
  */
13
- interface StartVerificationParams {
14
- /** Type of document to verify (e.g., IDENTITY_CARD, DRIVING_LICENSE) */
15
- documentType: 'IDENTITY_CARD' | 'DRIVING_LICENSE';
16
- /** Country code (e.g., MZ, AO, PT) */
17
- country: string;
18
- /** Optional existing identity ID to link verification to */
19
- identityId?: string;
20
- /** Optional external reference ID */
21
- externalId?: string;
42
+ declare function isReactNative(): boolean;
43
+ /**
44
+ * The real image type, from the first bytes.
45
+ *
46
+ * The gateway sniffs the same way and ignores the declared Content-Type, so a
47
+ * caller who mislabels a PNG as image/jpeg still succeeds — and one who
48
+ * uploads a PDF renamed to .jpg still fails. Doing it here just moves that
49
+ * answer earlier.
50
+ */
51
+ declare function detectImageMimeType(bytes: Uint8Array): ImageMimeType | null;
52
+ /** Decodes base64 using whichever primitive the runtime provides. */
53
+ declare function decodeBase64(input: string): Uint8Array;
54
+ /** Encodes bytes as base64 using whichever primitive the runtime provides. */
55
+ declare function encodeBase64(bytes: Uint8Array): string;
56
+ /**
57
+ * Turns whatever the caller has into something FormData can carry on this
58
+ * platform, validating the bytes first whenever they are visible.
59
+ *
60
+ * A React Native `file://` URI is the one case where they are not: the file
61
+ * lives on disk and only the native layer reads it, so type and size are left
62
+ * to the gateway to enforce.
63
+ *
64
+ * @param input The image, in any supported form
65
+ * @param defaultName Filename to use when the input does not carry one
66
+ */
67
+ declare function normalizeFile(input: FileInput, defaultName: string): NormalizedFile;
68
+ /**
69
+ * {@link normalizeFile}, but able to read a Blob's first bytes.
70
+ *
71
+ * This is what the upload path uses. A file chosen from an `<input>` arrives
72
+ * as a Blob whose `type` is whatever the operating system guessed from the
73
+ * extension, so a PDF renamed `.jpg` claims to be `image/jpeg` — and the
74
+ * synchronous path has to take that on trust. Reading eight bytes settles it
75
+ * before megabytes go over a phone connection, using the same magic-byte test
76
+ * the gateway applies on arrival.
77
+ *
78
+ * Every other input form is already validated synchronously, so it is simply
79
+ * delegated.
80
+ */
81
+ declare function prepareFile(input: FileInput, defaultName: string): Promise<NormalizedFile>;
82
+
83
+ /**
84
+ * Metadata the gateway attaches to every response, success or error.
85
+ *
86
+ * `requestId` is also returned as the X-Request-Id header and is the value to
87
+ * quote when reporting a problem — it correlates the call with the gateway's
88
+ * own logs.
89
+ */
90
+ interface ApiMeta {
91
+ requestId: string;
92
+ timestamp: string;
93
+ /** The gateway's API version, not the SDK's. */
94
+ version: string;
95
+ }
96
+ interface PaginationMeta {
97
+ page: number;
98
+ limit: number;
99
+ total: number;
22
100
  }
23
- interface FaceMatchVerificationParams {
24
- externalId: string;
25
- file: string;
26
- mimeType?: string;
101
+ /** A successful response, unwrapped. */
102
+ interface ApiResult<T> {
103
+ data: T;
104
+ meta: ApiMeta;
105
+ /** Present only on list endpoints. */
106
+ pagination?: PaginationMeta;
27
107
  }
28
- interface FaceMatchStatusParams {
29
- externalId: string;
30
- faceCheckId: string;
31
- interval?: number;
32
- timeout?: number;
108
+ /** A page of results: the rows, plus the pagination block that came with them. */
109
+ interface Page<T> {
110
+ items: T[];
111
+ pagination: PaginationMeta;
33
112
  }
34
113
  /**
35
- * Response from starting a verification session
114
+ * Turns an unwrapped list response into a Page.
115
+ *
116
+ * The gateway sends the rows as `data` and the counts as a sibling
117
+ * `pagination` block; a caller wants them together. When the endpoint returned
118
+ * no pagination block (or an unpaginated array), the totals are derived from
119
+ * the rows so the shape stays uniform.
36
120
  */
37
- interface VerificationSession {
38
- /** Unique verification ID */
39
- verificationId: string;
40
- /** Linked identity ID (if exists) */
41
- identityId?: string;
42
- /** Current verification status */
43
- status: string;
121
+ declare function toPage<T>(result: ApiResult<T[]>): Page<T>;
122
+
123
+ /** Returns a verification token, refreshing it if the cached one has expired. */
124
+ type VerificationTokenProvider = () => string | Promise<string>;
125
+ /** The subset of `fetch` the SDK uses, so a custom implementation can stand in. */
126
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
127
+ /** Which credential an endpoint accepts. */
128
+ type AuthMode = 'api-key' | 'token' | 'any' | 'none';
129
+ interface HttpClientConfig {
130
+ /** Origin of the gateway, e.g. `https://api.kyciris.com`. */
131
+ baseUrl: string;
132
+ /**
133
+ * Project API key. A project-wide credential — server-side only. Passing it
134
+ * from a browser or React Native app throws CREDENTIAL_MISUSE.
135
+ */
136
+ apiKey?: string;
137
+ /**
138
+ * Verification token, scoped to one end user. This is the credential a
139
+ * client app should hold: mint it on your server with
140
+ * `client.verifications.createToken(externalId)` and hand it out.
141
+ *
142
+ * A function is called before each request, so a token can be refreshed
143
+ * without rebuilding the client.
144
+ */
145
+ verificationToken?: string | VerificationTokenProvider;
146
+ /** URI version segment. Defaults to `v1`. */
147
+ apiVersion?: string;
148
+ /** Per-attempt timeout in ms. Defaults to 30000. */
149
+ timeoutMs?: number;
150
+ /** Retries for a failed attempt. Defaults to 2 (three attempts in total). */
151
+ maxRetries?: number;
152
+ /** Custom fetch (tests, proxies, a Node agent). Defaults to global fetch. */
153
+ fetch?: FetchLike;
154
+ /** Extra headers on every request. Cannot override the auth headers. */
155
+ headers?: Record<string, string>;
156
+ /**
157
+ * Allows an `http://` baseUrl to a non-loopback host.
158
+ *
159
+ * Off by default: the requests carry a project API key or a verification
160
+ * token and the responses carry identity-document data, none of which may
161
+ * cross a plaintext connection.
162
+ */
163
+ allowInsecureBaseUrl?: boolean;
164
+ /**
165
+ * Allows `apiKey` in a browser or React Native app.
166
+ *
167
+ * Only for a trusted first-party context (an internal operations console
168
+ * behind a login). Anything shipped to end users must use a verification
169
+ * token: an API key in a client bundle is extractable and grants the whole
170
+ * project, every identity in it included.
171
+ */
172
+ allowApiKeyInUntrustedClient?: boolean;
173
+ }
174
+ interface RequestOptions {
175
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
176
+ /** Path below the version segment, e.g. `/verification/start`. */
177
+ path: string;
178
+ query?: Record<string, unknown>;
179
+ /** JSON request body. Mutually exclusive with `form`. */
180
+ body?: unknown;
181
+ /** Multipart body. Values typed `FileInput` are normalized per platform. */
182
+ form?: Record<string, string | FileInput | undefined>;
183
+ /** Field names in `form` that hold a file. */
184
+ fileFields?: string[];
185
+ auth?: AuthMode;
186
+ signal?: AbortSignal;
187
+ /** Overrides the client's timeout for this call. */
188
+ timeoutMs?: number;
189
+ /**
190
+ * Whether a failed attempt may be repeated.
191
+ *
192
+ * Defaults to true for GET and false for everything else: a POST that timed
193
+ * out may well have been processed, and re-sending an upload would emit a
194
+ * second pipeline event. A 429 is retried regardless of method, because the
195
+ * gateway rejected it before doing any work.
196
+ */
197
+ retry?: boolean;
44
198
  }
45
199
  /**
46
- * Parameters for uploading a selfie image
200
+ * True in a context where the bundle is delivered to an end user and anything
201
+ * inside it is readable by them: a browser page or a React Native app.
47
202
  */
48
- interface UploadSelfieParams {
49
- /** Verification ID to attach the selfie to */
50
- verificationId: string;
51
- /** Base64 encoded image data, data URI, or file:// URI (React Native) */
52
- imageData: string;
53
- /** MIME type of the image (default: image/jpeg) */
54
- mimeType?: string;
203
+ declare function isUntrustedClient(): boolean;
204
+ /**
205
+ * The gateway's HTTP transport: URL and auth assembly, timeouts, retries, and
206
+ * translation of the `{ data, meta }` / `{ error, meta }` envelopes into
207
+ * values and KycirisErrors.
208
+ */
209
+ declare class HttpClient {
210
+ private readonly baseUrl;
211
+ private readonly apiVersion;
212
+ private readonly timeoutMs;
213
+ private readonly maxRetries;
214
+ private readonly fetchImpl;
215
+ private readonly extraHeaders;
216
+ private readonly apiKey?;
217
+ private readonly tokenSource?;
218
+ constructor(config: HttpClientConfig);
219
+ /** True when the client holds a project API key. */
220
+ hasApiKey(): boolean;
221
+ /** True when the client holds (or can fetch) a verification token. */
222
+ hasVerificationToken(): boolean;
223
+ buildUrl(path: string, query?: Record<string, unknown>): string;
224
+ private authHeaders;
225
+ private resolveToken;
226
+ private buildForm;
227
+ /** Runs a request, retrying where the method and failure allow it. */
228
+ request<T>(options: RequestOptions): Promise<ApiResult<T>>;
229
+ private attempt;
230
+ /**
231
+ * Streams a response body instead of parsing it — the media download
232
+ * endpoint returns an image, not an envelope.
233
+ */
234
+ requestRaw(options: RequestOptions): Promise<Response>;
55
235
  }
236
+
237
+ /**
238
+ * Domain types mirroring the KYCiris gateway's public contract.
239
+ *
240
+ * Everything here is derived from the gateway's DTOs and constants; where the
241
+ * gateway is the authority for a set of values (document catalog, statuses,
242
+ * webhook events) the list is reproduced verbatim rather than widened to
243
+ * `string`, so a typo fails at compile time instead of at runtime with a 422.
244
+ */
245
+ /**
246
+ * The (country, documentType) pairs the pipeline can actually process.
247
+ *
248
+ * Mirrors DOCUMENT_CATALOG in the gateway, which in turn mirrors the OCR
249
+ * worker's handler registry — a pair is only real once something knows how to
250
+ * read it. Starting a verification for a pair outside this list is rejected by
251
+ * the gateway with VALIDATION_ERROR.
252
+ */
253
+ declare const DOCUMENT_CATALOG: readonly [{
254
+ readonly country: "AO";
255
+ readonly documentType: "ID_CARD";
256
+ }, {
257
+ readonly country: "AO";
258
+ readonly documentType: "DRIVING_LICENSE";
259
+ }, {
260
+ readonly country: "MZ";
261
+ readonly documentType: "ID_CARD";
262
+ }];
263
+ type DocumentCatalogEntry = (typeof DOCUMENT_CATALOG)[number];
264
+ /** ISO 3166-1 alpha-2 codes present in the catalog. */
265
+ type CatalogCountry = DocumentCatalogEntry['country'];
266
+ /** Document type codes present in the catalog. */
267
+ type DocumentType = DocumentCatalogEntry['documentType'];
268
+ /** True when the gateway can process this pair. */
269
+ declare function isSupportedDocumentPair(country: string, documentType: string): boolean;
270
+ /** The countries in the catalog, deduplicated. */
271
+ declare function supportedCountries(): CatalogCountry[];
272
+ /** The document types the catalog supports for `country`. */
273
+ declare function supportedDocumentTypes(country: string): DocumentType[];
274
+ /** The pipeline's state machine, as written by the gateway and its workers. */
275
+ declare const VERIFICATION_STATUSES: readonly ["PENDING", "PROCESSING", "REVIEW", "APPROVED", "REJECTED"];
276
+ type VerificationStatus = (typeof VERIFICATION_STATUSES)[number];
56
277
  /**
57
- * Parameters for uploading a document image
278
+ * What a verification *means* operationally, derived by the gateway from the
279
+ * status and rejection code.
280
+ *
281
+ * The distinction that `status` alone cannot make: REJECTED is written both
282
+ * when the rules genuinely said no and when our own OCR choked on a valid
283
+ * document. Those want opposite handling — the first is an answer for the end
284
+ * user, the second is a retry for us.
58
285
  */
59
- interface UploadDocumentParams {
60
- /** Verification ID to attach the document to */
286
+ declare const VERIFICATION_OUTCOMES: readonly ["APPROVED", "REJECTED", "FAILED_TECHNICAL", "NEEDS_REVIEW"];
287
+ type VerificationOutcome = (typeof VERIFICATION_OUTCOMES)[number];
288
+ /** Statuses from which the pipeline will not move on its own. */
289
+ declare const TERMINAL_VERIFICATION_STATUSES: readonly ["APPROVED", "REJECTED"];
290
+ /** True when the pipeline has reached a decision for this status. */
291
+ declare function isTerminalStatus(status: string): boolean;
292
+ /** A step the end user has to complete before the pipeline can decide. */
293
+ type VerificationStep = 'document_front' | 'document_back' | 'selfie';
294
+ /** The side of an identity document being uploaded. */
295
+ type DocumentSide = 'front' | 'back';
296
+ /** Files a verification can hold, as named by the gateway's media endpoints. */
297
+ type MediaResource = 'selfie' | 'document-front' | 'document-back' | 'document-face';
298
+ interface StartVerificationParams {
299
+ /** Must form a supported pair with `country` (see DOCUMENT_CATALOG). */
300
+ documentType: DocumentType | (string & {});
301
+ /** ISO 3166-1 alpha-2 country code. */
302
+ country: CatalogCountry | (string & {});
303
+ /** Link to an existing identity instead of creating a new one. */
304
+ identityId?: string;
305
+ /**
306
+ * Your own reference for this end user.
307
+ *
308
+ * Ignored when the client authenticates with a verification token: that token
309
+ * is already bound to one externalId, and the gateway treats the token's
310
+ * value as authoritative so a token cannot create a verification under
311
+ * someone else's reference.
312
+ */
313
+ externalId?: string;
314
+ /**
315
+ * Name of the verification level (policy) to run under. Required.
316
+ *
317
+ * The level is what says which documents the project accepts and whether a
318
+ * selfie is needed, so the gateway has no verification without one: a start
319
+ * that names no level, or names one that is not active on the project, is
320
+ * rejected (422 / 404). Levels are created from your backend with
321
+ * `client.levels.create()` -- a project can verify nobody until it has one.
322
+ */
323
+ levelName: string;
324
+ }
325
+ interface VerificationSession {
326
+ verificationId: string;
327
+ identityId?: string;
328
+ status: VerificationStatus;
329
+ }
330
+ /** OCR output. Fields present depend on the document; unread fields are null. */
331
+ interface OcrData {
332
+ country?: string | null;
333
+ documentType?: string | null;
334
+ fullName?: string | null;
335
+ idNumber?: string | null;
336
+ birthDate?: string | null;
337
+ validFrom?: string | null;
338
+ validUntil?: string | null;
339
+ gender?: string | null;
340
+ height?: number | null;
341
+ address?: string | null;
342
+ mrz?: string | null;
343
+ maritalStatus?: string | null;
344
+ [field: string]: unknown;
345
+ }
346
+ interface VerificationStatusResponse {
61
347
  verificationId: string;
62
- /** Type of document (front or back) */
63
- type: 'front' | 'back';
64
- /** Base64 encoded image data, data URI, or file:// URI (React Native) */
65
- imageData: string;
66
- /** MIME type of the image (default: image/jpeg) */
67
- mimeType?: string;
68
- }
69
- /** Result of an upload operation */
348
+ status: VerificationStatus;
349
+ /** Null while still PENDING/PROCESSING — no outcome exists yet. */
350
+ outcome: VerificationOutcome | null;
351
+ /** 0-1 similarity between selfie and document photo; null until computed. */
352
+ faceMatchScore: number | null;
353
+ /** Null until the document front has been processed. */
354
+ ocrData: OcrData | null;
355
+ /** A gateway ERROR_CODE naming why, when the pipeline stopped or refused. */
356
+ rejectionType: string | null;
357
+ rejectionReason: string | null;
358
+ /** An operator's email, "system", or null. */
359
+ decidedBy: string | null;
360
+ decidedAt: string | null;
361
+ createdAt: string;
362
+ updatedAt: string;
363
+ }
70
364
  interface UploadResult {
71
- /** Response message */
72
365
  message: string;
73
- /** Current status after upload */
74
- status: string;
366
+ status: VerificationStatus;
75
367
  }
76
- /** Current status of a verification session */
77
- interface VerificationStatus {
78
- /** Verification ID */
79
- verificationId: string;
80
- /** Current status: PENDING, PROCESSING, APPROVED, or REJECTED */
81
- status: 'PENDING' | 'PROCESSING' | 'APPROVED' | 'REJECTED';
82
- /** Face match similarity score (0-1) */
83
- faceMatchScore?: number;
84
- /** OCR extracted data from document */
85
- ocrData?: {
86
- /** Full name extracted from document */
87
- fullName?: string;
88
- /** ID number extracted from document */
89
- idNumber?: string;
90
- /** Birth date extracted from document */
91
- birthDate?: string;
92
- /** Expiry date extracted from document */
93
- expiryDate?: string;
94
- /** Additional extracted fields */
95
- [key: string]: any;
368
+ /** One row of the operations list. Deliberately free of OCR data and paths. */
369
+ interface VerificationListItem {
370
+ id: string;
371
+ externalId: string | null;
372
+ status: VerificationStatus;
373
+ outcome: VerificationOutcome | null;
374
+ /** True when it has sat in flight past the gateway's stall threshold. */
375
+ stalled: boolean;
376
+ country: string | null;
377
+ documentType: string | null;
378
+ faceMatchScore: number | null;
379
+ rejectionType: string | null;
380
+ rejectionReason: string | null;
381
+ decidedBy: string | null;
382
+ decidedAt: string | null;
383
+ createdAt: string;
384
+ updatedAt: string;
385
+ }
386
+ interface ListVerificationsParams {
387
+ page?: number;
388
+ /** 1-100; the gateway rejects anything larger. */
389
+ limit?: number;
390
+ status?: VerificationStatus[];
391
+ outcome?: VerificationOutcome;
392
+ /** Only stalled (true) or only healthy (false). Omit for both. */
393
+ stalled?: boolean;
394
+ country?: string;
395
+ documentType?: string;
396
+ /** Exact match on your own reference. */
397
+ externalId?: string;
398
+ /** ISO 8601 instant. */
399
+ createdAfter?: string;
400
+ /** ISO 8601 instant. */
401
+ createdBefore?: string;
402
+ order?: 'ASC' | 'DESC';
403
+ }
404
+ interface VerificationSummary {
405
+ byStatus: Partial<Record<VerificationStatus, number>>;
406
+ byOutcome: Partial<Record<VerificationOutcome, number>>;
407
+ needsAttention: {
408
+ stalled: number;
409
+ failedTechnical: number;
410
+ needsReview: number;
411
+ parkedMessages: number;
96
412
  };
97
- /** When the verification was created */
413
+ }
414
+ /** What kind of thing one history entry records. */
415
+ declare const PIPELINE_EVENT_KINDS: readonly ["CREATED", "STATUS_CHANGED", "REPROCESSED", "JOB_PARKED", "JOB_RESENT", "JOB_DISCARDED"];
416
+ type PipelineEventKind = (typeof PIPELINE_EVENT_KINDS)[number];
417
+ /**
418
+ * One entry in a verification's history, appended and never updated.
419
+ *
420
+ * The verification row keeps only the last decision, so this is the only place
421
+ * the sequence survives. Entries that predate the history simply do not exist —
422
+ * a verification from before it shows its creation and last decision and
423
+ * nothing between.
424
+ */
425
+ interface PipelineEvent {
426
+ id: string;
427
+ kind: PipelineEventKind | (string & {});
428
+ fromStatus: string | null;
429
+ toStatus: string | null;
430
+ /**
431
+ * Who did it, as a label frozen at the time — an operator's email, or
432
+ * "system". A snapshot, so it stays true after the person is renamed or
433
+ * removed.
434
+ */
435
+ actorLabel: string;
436
+ actorUserId: string | null;
437
+ /** The one thing worth reading beside the transition. */
438
+ detail: string | null;
439
+ createdAt: string;
440
+ [field: string]: unknown;
441
+ }
442
+ interface ReviewDecision {
443
+ decision: 'APPROVED' | 'REJECTED';
444
+ /** Stored when rejecting. */
445
+ reason?: string;
446
+ }
447
+ interface IdentityDocumentStep {
448
+ step: 'IDENTITY_DOCUMENT';
449
+ required: true;
450
+ documents: {
451
+ country: string;
452
+ types: string[];
453
+ }[];
454
+ }
455
+ interface SelfieStep {
456
+ step: 'SELFIE';
457
+ required: boolean;
458
+ /** Accepted but not enforced by the gateway yet. */
459
+ liveness?: boolean;
460
+ }
461
+ type VerificationLevelStep = IdentityDocumentStep | SelfieStep;
462
+ interface VerificationLevelDefinition {
463
+ requiredSteps: VerificationLevelStep[];
464
+ }
465
+ interface VerificationLevel {
466
+ id: string;
467
+ name: string;
468
+ isActive: boolean;
469
+ definition: VerificationLevelDefinition;
470
+ createdAt: string;
471
+ updatedAt: string;
472
+ }
473
+ interface IdentityFields {
474
+ mrz?: string;
475
+ gender?: string;
476
+ height?: number;
477
+ address?: string;
478
+ country?: string;
479
+ fullName?: string;
480
+ idNumber?: string;
481
+ birthDate?: string;
482
+ maritalStatus?: string;
483
+ }
484
+ interface IdentitySummary extends IdentityFields {
485
+ id: string;
98
486
  createdAt: string;
99
- /** When the verification was last updated */
100
487
  updatedAt: string;
101
488
  }
102
- /** Event emitted when KYC status changes */
103
- interface KYCStatusEvent {
104
- /** Type of event: statusChanged or error */
105
- type: 'statusChanged' | 'error';
106
- /** New status (for statusChanged events) */
107
- status?: string;
108
- /** Error message (for error events) */
109
- error?: string;
489
+ interface Identity extends IdentitySummary {
490
+ verifications?: VerificationListItem[];
491
+ [field: string]: unknown;
492
+ }
493
+ interface ListIdentitiesParams {
494
+ page?: number;
495
+ limit?: number;
496
+ /** Partial match. */
497
+ fullName?: string;
498
+ country?: string;
499
+ idNumber?: string;
500
+ }
501
+ /** Fields the gateway can group duplicate identities by. */
502
+ declare const DUPLICATE_FIELDS: readonly ["fullName", "idNumber", "mrz", "gender", "country", "birthDate"];
503
+ type DuplicateField = (typeof DUPLICATE_FIELDS)[number];
504
+ interface FaceCheck {
505
+ id: string;
506
+ status: 'PENDING' | 'APPROVED' | 'REJECTED';
507
+ }
508
+ type OcrProcessStatus = 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED';
509
+ interface OcrProcess {
510
+ id: string;
511
+ status: OcrProcessStatus;
512
+ documentType: string;
513
+ country: string;
514
+ createdAt: string;
515
+ }
516
+ interface OcrProcessStatusResponse {
517
+ id: string;
518
+ status: OcrProcessStatus;
519
+ /** Present once COMPLETED. */
520
+ ocrResult?: OcrData;
521
+ documentPath?: string;
522
+ createdAt: string;
523
+ updatedAt: string;
524
+ }
525
+ declare const WEBHOOK_EVENT_TYPES: readonly ["VERIFICATION_STARTED", "VERIFICATION_COMPLETED", "VERIFICATION_APPROVED", "VERIFICATION_REJECTED", "VERIFICATION_REVIEW_REQUIRED", "OCR_COMPLETED", "OCR_FAILED"];
526
+ type WebhookEventType = (typeof WEBHOOK_EVENT_TYPES)[number];
527
+ /**
528
+ * Subscribes to every event, including ones added after the endpoint was
529
+ * registered. Prefer it over listing all seven when the intent is "mirror my
530
+ * verification lifecycle" — a literal list silently stops being complete.
531
+ */
532
+ declare const ALL_WEBHOOK_EVENTS = "*";
533
+ type WebhookSubscription = WebhookEventType | typeof ALL_WEBHOOK_EVENTS;
534
+ interface Webhook {
535
+ id: string;
536
+ url: string;
537
+ eventTypes: WebhookSubscription[];
538
+ isActive: boolean;
539
+ headers?: Record<string, string> | null;
540
+ maxRetries?: number | null;
541
+ createdAt: string;
542
+ updatedAt: string;
543
+ }
544
+ interface CreateWebhookParams {
545
+ /** Must be https:// with a public hostname — the gateway refuses anything else. */
546
+ url: string;
547
+ eventTypes: WebhookSubscription[];
548
+ headers?: Record<string, string>;
549
+ maxRetries?: number;
550
+ }
551
+ interface UpdateWebhookParams {
552
+ url?: string;
553
+ /** Replaces the current list entirely. */
554
+ eventTypes?: WebhookSubscription[];
555
+ isActive?: boolean;
556
+ headers?: Record<string, string>;
557
+ maxRetries?: number;
558
+ }
559
+ /**
560
+ * The result of replaying a delivery.
561
+ *
562
+ * A failed send is a value, not an exception: the gateway records a FAILED row
563
+ * for the attempt either way, so the caller almost always wants to show why
564
+ * beside the delivery it belongs to rather than catch around it.
565
+ */
566
+ type WebhookResendResult = {
567
+ success: true;
568
+ } | {
569
+ success: false;
570
+ error: string;
571
+ };
572
+ interface WebhookLogEntry {
573
+ id: string;
574
+ url: string;
575
+ status: string;
576
+ responseStatus?: number | null;
577
+ responseBody?: string | null;
578
+ payload?: Record<string, unknown>;
579
+ headers?: Record<string, string> | null;
580
+ retries?: number | null;
581
+ createdAt: string;
582
+ updatedAt: string;
583
+ [field: string]: unknown;
584
+ }
585
+ interface AnalyticsSeries {
586
+ change_percentage: number;
587
+ /** Keyed by month, e.g. "2026-07". */
588
+ monthly: Record<string, number>;
589
+ total: number;
590
+ }
591
+ interface Analytics {
592
+ identity: AnalyticsSeries;
593
+ verification: AnalyticsSeries;
594
+ }
595
+
596
+ /** Monthly identity and verification counts, with month-over-month change. */
597
+ declare class AnalyticsResource {
598
+ private readonly http;
599
+ constructor(http: HttpClient);
600
+ get(options?: {
601
+ signal?: AbortSignal;
602
+ }): Promise<Analytics>;
603
+ }
604
+
605
+ interface PollOptions {
606
+ /** Delay between attempts, in ms. Defaults to 3000. */
607
+ intervalMs?: number;
608
+ /** Give up after this long, in ms. Defaults to 120000. */
609
+ timeoutMs?: number;
610
+ /** Cancels the poll; the promise rejects with code ABORTED. */
611
+ signal?: AbortSignal;
612
+ }
613
+ interface PollCallbacks<T> {
614
+ /** Called with every intermediate value, before the done check. */
615
+ onUpdate?: (value: T) => void;
616
+ }
617
+ /**
618
+ * Calls `fetchOnce` until `isDone` accepts the result, the timeout elapses, or
619
+ * the caller aborts.
620
+ *
621
+ * Written as a loop with an awaited delay rather than a self-scheduling
622
+ * setTimeout so that a cancelled poll actually stops: the previous
623
+ * implementation left a pending timer behind after rejecting, which in React
624
+ * Native keeps the JS context awake and warns about a setState on an unmounted
625
+ * component.
626
+ *
627
+ * The timeout is checked before sleeping as well as after, so a poll never
628
+ * waits out one final interval it has no time left to use.
629
+ */
630
+ declare function pollUntil<T>(fetchOnce: (signal?: AbortSignal) => Promise<T>, isDone: (value: T) => boolean, options?: PollOptions & PollCallbacks<T>, description?: string): Promise<T>;
631
+
632
+ interface DuplicateGroup {
633
+ value: string;
634
+ count: number;
635
+ identities: IdentitySummary[];
636
+ }
637
+ interface DuplicatesByField {
638
+ field: string;
639
+ totalDuplicateGroups: number;
640
+ totalDuplicateIdentities: number;
641
+ duplicates: DuplicateGroup[];
642
+ }
643
+ interface DuplicatesByFields {
644
+ fields: string[];
645
+ results: Record<string, DuplicatesByField>;
110
646
  }
111
- /** Callback function for handling KYC status events */
112
- type KYCEventCallback = (event: KYCStatusEvent) => void;
113
647
  /**
114
- * Custom error class for KYC SDK errors
648
+ * The identity records behind verifications — the person, as opposed to any
649
+ * one attempt to verify them.
650
+ *
651
+ * Every endpoint here needs the project API key. A verification token is
652
+ * scoped to a single end user's flow and is refused, which is deliberate: an
653
+ * identity record holds the full extracted document data, and a client app has
654
+ * no business reading the project's identities.
115
655
  */
116
- declare class KYCSdkError extends Error {
117
- /** Error code for programmatic error handling */
118
- code: string;
119
- /** HTTP status code if available */
120
- statusCode?: number;
656
+ declare class IdentitiesResource {
657
+ private readonly http;
658
+ constructor(http: HttpClient);
659
+ /** Registers an identity independently of any verification. */
660
+ create(fields: IdentityFields, options?: {
661
+ signal?: AbortSignal;
662
+ }): Promise<Identity>;
663
+ /** A page of identity summaries. Read one identity for the full record. */
664
+ list(params?: ListIdentitiesParams, options?: {
665
+ signal?: AbortSignal;
666
+ }): Promise<Page<IdentitySummary>>;
667
+ /** The full record, including its verification history. */
668
+ get(id: string, options?: {
669
+ signal?: AbortSignal;
670
+ }): Promise<Identity>;
671
+ /** This identity's verifications, without embeddings or storage paths. */
672
+ verifications(id: string, options?: {
673
+ signal?: AbortSignal;
674
+ }): Promise<VerificationListItem[]>;
675
+ update(id: string, fields: IdentityFields, options?: {
676
+ signal?: AbortSignal;
677
+ }): Promise<Identity>;
678
+ /** Deletes the identity, its verifications, and the stored images. */
679
+ delete(id: string, options?: {
680
+ signal?: AbortSignal;
681
+ }): Promise<void>;
682
+ /**
683
+ * Groups identities that share a value, to find duplicate registrations.
684
+ *
685
+ * Passing one field returns a single grouping; passing several returns one
686
+ * per field, which is why the return type is a union.
687
+ */
688
+ findDuplicates(params?: {
689
+ field?: DuplicateField | DuplicateField[];
690
+ id?: string;
691
+ }, options?: {
692
+ signal?: AbortSignal;
693
+ }): Promise<DuplicatesByField | DuplicatesByFields>;
694
+ /**
695
+ * Folds duplicates into a primary identity, moving their verifications
696
+ * across and deleting them. Requires the admin role on a user token; an API
697
+ * key is a project credential and carries no role.
698
+ */
699
+ merge(id: string, duplicateIds: string[], options?: {
700
+ signal?: AbortSignal;
701
+ }): Promise<unknown>;
702
+ /**
703
+ * Compares a fresh photo against the face already on file for an identity —
704
+ * a re-authentication check, separate from the verification flow.
705
+ *
706
+ * Asynchronous: the returned check starts PENDING. Poll it with
707
+ * `faceCheckStatus`, or use `waitForFaceCheck`.
708
+ */
709
+ faceMatch(id: string, file: FileInput, options?: {
710
+ signal?: AbortSignal;
711
+ timeoutMs?: number;
712
+ }): Promise<FaceCheck>;
713
+ /** The result of a face-match check started with `faceMatch`. */
714
+ faceCheckStatus(id: string, faceCheckId: string, options?: {
715
+ signal?: AbortSignal;
716
+ }): Promise<FaceCheck>;
717
+ /** Polls a face-match check until it is APPROVED or REJECTED. */
718
+ waitForFaceCheck(id: string, faceCheckId: string, options?: PollOptions & PollCallbacks<FaceCheck>): Promise<FaceCheck>;
719
+ }
720
+
721
+ interface CreateLevelParams {
722
+ /** Unique within the project. */
723
+ name: string;
724
+ definition: VerificationLevelDefinition;
725
+ isActive?: boolean;
726
+ }
727
+ interface UpdateLevelParams {
728
+ name?: string;
729
+ /** Replaces the required steps entirely. */
730
+ definition?: VerificationLevelDefinition;
731
+ isActive?: boolean;
732
+ }
733
+ /**
734
+ * Verification levels: named, reusable policies saying which steps a
735
+ * verification requires and which documents it accepts.
736
+ *
737
+ * Every verification names one at start time (`levelName`), and there is no
738
+ * default: a project can verify nobody until it has at least one active level,
739
+ * and a definition must include an IDENTITY_DOCUMENT step saying which
740
+ * (country, documentType) pairs it accepts.
741
+ *
742
+ * API key only, and creating or changing one needs the admin role when acting
743
+ * as a user. Deleting is a soft delete, so verifications that already ran keep
744
+ * the rules they ran under.
745
+ */
746
+ declare class LevelsResource {
747
+ private readonly http;
748
+ constructor(http: HttpClient);
749
+ create(params: CreateLevelParams, options?: {
750
+ signal?: AbortSignal;
751
+ }): Promise<VerificationLevel>;
752
+ list(options?: {
753
+ signal?: AbortSignal;
754
+ }): Promise<VerificationLevel[]>;
755
+ get(id: string, options?: {
756
+ signal?: AbortSignal;
757
+ }): Promise<VerificationLevel>;
758
+ update(id: string, params: UpdateLevelParams, options?: {
759
+ signal?: AbortSignal;
760
+ }): Promise<VerificationLevel>;
761
+ delete(id: string, options?: {
762
+ signal?: AbortSignal;
763
+ }): Promise<void>;
764
+ }
765
+
766
+ interface DownloadToken {
767
+ token: string;
768
+ /** Lifetime in seconds. The gateway fixes it at 60. */
769
+ expiresIn: number;
770
+ }
771
+ /**
772
+ * Access to the images behind a verification: the selfie, both document pages,
773
+ * and the face the OCR worker cropped out of the document.
774
+ *
775
+ * Downloads are two calls on purpose. The first, authenticated with the project
776
+ * API key, issues a token for exactly one file; the second exchanges it for the
777
+ * bytes. The token lives 60 seconds, is invalidated the first time it is used,
778
+ * and every use is written to the gateway's media access log — so an image URL
779
+ * that leaks into a browser history, a screenshot or a support ticket is worth
780
+ * nothing by the time anyone finds it.
781
+ */
782
+ declare class MediaResourceApi {
783
+ private readonly http;
784
+ constructor(http: HttpClient);
785
+ /**
786
+ * Issues a single-use, 60-second token for one file of one verification.
787
+ *
788
+ * Useful on its own when the bytes should be fetched somewhere else — hand
789
+ * the token to a browser and let it call the download URL directly, without
790
+ * the API key ever leaving your server.
791
+ */
792
+ createDownloadToken(verificationId: string, resource: MediaResource, options?: {
793
+ signal?: AbortSignal;
794
+ }): Promise<DownloadToken>;
795
+ /**
796
+ * The URL that exchanges a download token for the file.
797
+ *
798
+ * Treat it as a credential: the token is the whole authorization, so the URL
799
+ * grants the image to whoever holds it, once.
800
+ */
801
+ downloadUrl(token: string): string;
802
+ /**
803
+ * Fetches a file, taking care of both steps.
804
+ *
805
+ * Returns the raw Response so a caller can stream it (to disk, or straight
806
+ * into an HTTP response) instead of holding an identity document in memory.
807
+ */
808
+ download(verificationId: string, resource: MediaResource, options?: {
809
+ signal?: AbortSignal;
810
+ }): Promise<Response>;
811
+ /** Fetches a file and buffers it. Convenient; holds the image in memory. */
812
+ downloadBytes(verificationId: string, resource: MediaResource, options?: {
813
+ signal?: AbortSignal;
814
+ }): Promise<Uint8Array>;
815
+ }
816
+
817
+ interface RunOcrParams {
818
+ documentType: DocumentType | (string & {});
819
+ country: CatalogCountry | (string & {});
820
+ /** Your own reference, for correlating the result. */
821
+ externalId?: string;
822
+ }
823
+ /**
824
+ * Standalone document reading, with no verification attached.
825
+ *
826
+ * The same OCR pipeline the verification flow uses, addressed directly: upload
827
+ * a document, get the structured fields back. Nothing is decided and no
828
+ * identity is created — for that, run a verification.
829
+ */
830
+ declare class OcrResource {
831
+ private readonly http;
832
+ constructor(http: HttpClient);
833
+ /**
834
+ * Queues a document for extraction.
835
+ *
836
+ * Returns as soon as the job is accepted; the fields arrive later, via
837
+ * `getStatus` or `waitForResult`.
838
+ */
839
+ run(file: FileInput, params: RunOcrParams, options?: {
840
+ signal?: AbortSignal;
841
+ timeoutMs?: number;
842
+ }): Promise<OcrProcess>;
843
+ /** Status of a queued job, with the extracted fields once COMPLETED. */
844
+ getStatus(ocrId: string, options?: {
845
+ signal?: AbortSignal;
846
+ }): Promise<OcrProcessStatusResponse>;
847
+ /** Polls until the job is COMPLETED or FAILED. */
848
+ waitForResult(ocrId: string, options?: PollOptions & PollCallbacks<OcrProcessStatusResponse>): Promise<OcrProcessStatusResponse>;
849
+ }
850
+
851
+ /**
852
+ * Pluggable key/value storage, so a verification that was interrupted can be
853
+ * picked up again after the app is closed.
854
+ *
855
+ * Inject AsyncStorage on React Native, or an adapter around localStorage on
856
+ * web. Methods may be synchronous or asynchronous; results are awaited either
857
+ * way.
858
+ */
859
+ interface KycirisStorage {
860
+ getItem(key: string): string | null | Promise<string | null>;
861
+ setItem(key: string, value: string): void | Promise<void>;
862
+ removeItem(key: string): void | Promise<void>;
863
+ }
864
+ /** Versioned, so a future change of shape cannot read an old record wrongly. */
865
+ declare const SESSION_STORAGE_KEY = "kyciris:session:v1";
866
+ /**
867
+ * What the SDK remembers about an in-flight verification.
868
+ *
869
+ * Deliberately holds no credential. The verification token is a bearer
870
+ * credential for one end user and persisting it would leave it in
871
+ * AsyncStorage/localStorage long after the flow ended, readable by anything
872
+ * else in the app; the host application decides where — and whether — to keep
873
+ * it. Only identifiers live here, and all of them are already known to the
874
+ * client that fetched them.
875
+ */
876
+ interface StoredSession {
877
+ verificationId: string;
878
+ identityId?: string;
879
+ externalId?: string;
880
+ documentType?: DocumentType | (string & {});
881
+ country?: CatalogCountry | (string & {});
882
+ levelName?: string;
883
+ /** ISO 8601 instant the verification was started. */
884
+ startedAt: string;
885
+ }
886
+ /**
887
+ * Reads and writes the resumable session.
888
+ *
889
+ * Every operation is best-effort: storage can be full, disabled (Safari private
890
+ * browsing), or simply absent. Losing the ability to resume is a degraded flow;
891
+ * throwing here would break a verification that is otherwise proceeding fine.
892
+ */
893
+ declare class SessionStore {
894
+ private readonly storage?;
895
+ constructor(storage?: KycirisStorage | undefined);
896
+ /** True when a storage adapter was configured. */
897
+ get enabled(): boolean;
898
+ save(session: StoredSession): Promise<void>;
899
+ read(): Promise<StoredSession | null>;
900
+ clear(): Promise<void>;
901
+ }
902
+ /**
903
+ * A storage adapter backed by a plain Map.
904
+ *
905
+ * For tests, and for a flow that should not outlive the process.
906
+ */
907
+ declare function createMemoryStorage(): KycirisStorage;
908
+
909
+ interface VerificationToken {
910
+ token: string;
911
+ /** The lifetime the gateway chose, e.g. "8h". The caller does not pick it. */
912
+ expiresIn: string;
913
+ }
914
+ interface AvailableMedia {
915
+ available: MediaResource[];
916
+ }
917
+ interface UploadOptions {
918
+ signal?: AbortSignal;
919
+ timeoutMs?: number;
920
+ }
921
+ interface VerificationProgress {
922
+ verificationId: string;
923
+ status: VerificationStatusResponse['status'];
924
+ outcome: VerificationStatusResponse['outcome'];
925
+ uploaded: Record<VerificationStep, boolean>;
926
+ /** The steps still to collect, in the order they should be asked for. */
927
+ missingSteps: VerificationStep[];
928
+ /** True when nothing is left to upload. */
929
+ isComplete: boolean;
930
+ }
931
+ interface ProgressOptions {
932
+ /**
933
+ * Whether the flow asks for a selfie. Defaults to true, matching the
934
+ * gateway's own default when no verification level is set.
935
+ *
936
+ * A client authenticated with a verification token cannot read the level it
937
+ * is running under — `/verification-levels` needs the project API key — so
938
+ * when a level makes the selfie optional, say so here.
939
+ */
940
+ selfieRequired?: boolean;
941
+ signal?: AbortSignal;
942
+ }
943
+ interface UploadStepParams {
944
+ step: VerificationStep;
945
+ file: FileInput;
946
+ /** Defaults to the stored session's verification. */
947
+ verificationId?: string;
948
+ /** See ProgressOptions.selfieRequired. */
949
+ selfieRequired?: boolean;
950
+ signal?: AbortSignal;
951
+ timeoutMs?: number;
952
+ }
953
+ interface UploadStepResult extends UploadResult {
954
+ /** True when the step was already uploaded and nothing was sent. */
955
+ skipped: boolean;
956
+ }
957
+ interface WaitForResultOptions extends PollOptions, PollCallbacks<VerificationStatusResponse> {
121
958
  /**
122
- * Creates a new KYC SDK error
123
- * @param message Human-readable error message
124
- * @param code Error code for handling
125
- * @param statusCode Optional HTTP status code
959
+ * Whether REVIEW ends the wait. Defaults to true.
960
+ *
961
+ * REVIEW means the automated pipeline has finished and handed the
962
+ * verification to a person, which can take hours. Polling through it almost
963
+ * always ends in POLL_TIMEOUT; subscribe to the VERIFICATION_APPROVED /
964
+ * VERIFICATION_REJECTED webhooks for the human's decision instead.
126
965
  */
127
- constructor(message: string, code: string, statusCode?: number);
966
+ stopOnReview?: boolean;
128
967
  }
129
968
  /**
130
- * Core KYC SDK client for interacting with the verification API
969
+ * The end-user verification flow, plus the review and operations endpoints
970
+ * that sit behind the project API key.
971
+ *
972
+ * Everything up to and including `getStatus` works with either credential; the
973
+ * list, summary, history and decision calls are API-key only and say so.
131
974
  */
132
- declare class KYCCore {
133
- private client;
134
- private credentials;
135
- private eventCallbacks;
975
+ declare class VerificationsResource {
976
+ private readonly http;
977
+ private readonly session;
978
+ constructor(http: HttpClient, session: SessionStore);
979
+ /**
980
+ * Mints a verification token for one end user.
981
+ *
982
+ * Call this on **your server**, with the project API key, and hand the token
983
+ * to the client app. The token authorizes exactly the flow below (start,
984
+ * upload, status) for the given `externalId` and nothing else, which is what
985
+ * keeps the project-wide API key out of a bundle an end user can read.
986
+ *
987
+ * The gateway owns the lifetime (VERIFICATION_TOKEN_TTL, 8h by default) — a
988
+ * client cannot ask for a longer one.
989
+ *
990
+ * @param externalId Your own reference for the end user
991
+ */
992
+ createToken(externalId: string, options?: {
993
+ signal?: AbortSignal;
994
+ }): Promise<VerificationToken>;
995
+ /**
996
+ * Starts a verification and, when a storage adapter is configured, records
997
+ * it so an interrupted flow can be resumed.
998
+ *
999
+ * The (country, documentType) pair and the level name are checked here before
1000
+ * the request goes out: the gateway rejects an unsupported pair with a 422
1001
+ * that names neither what is supported nor why, and a missing level with a
1002
+ * validation error that reads like a typo in your code rather than a policy
1003
+ * you have not created yet.
1004
+ */
1005
+ start(params: StartVerificationParams, options?: {
1006
+ signal?: AbortSignal;
1007
+ }): Promise<VerificationSession>;
1008
+ /** Uploads the end user's selfie. Triggers face matching once OCR has run. */
1009
+ uploadSelfie(verificationId: string, file: FileInput, options?: UploadOptions): Promise<UploadResult>;
1010
+ /**
1011
+ * Uploads one side of the identity document.
1012
+ *
1013
+ * OCR is queued once both sides are present, so a flow that collects only
1014
+ * the front never starts processing.
1015
+ */
1016
+ uploadDocument(verificationId: string, side: DocumentSide, file: FileInput, options?: UploadOptions): Promise<UploadResult>;
1017
+ /** Current status, face-match score and extracted document data. */
1018
+ getStatus(verificationId: string, options?: {
1019
+ signal?: AbortSignal;
1020
+ }): Promise<VerificationStatusResponse>;
1021
+ /**
1022
+ * Which files exist for a verification.
1023
+ *
1024
+ * This is the credential-safe way to tell what an end user has already
1025
+ * uploaded: it works with a verification token, whereas the identity record
1026
+ * that used to serve the same purpose is API-key only.
1027
+ */
1028
+ listMedia(verificationId: string, options?: {
1029
+ signal?: AbortSignal;
1030
+ }): Promise<MediaResource[]>;
1031
+ /**
1032
+ * Re-runs OCR and face matching from the files already uploaded.
1033
+ *
1034
+ * The remedy for a verification that failed technically (outcome
1035
+ * FAILED_TECHNICAL) or stalled. Clears the previous extraction and decision,
1036
+ * so it is not a read-only operation.
1037
+ *
1038
+ * Only the document front is required, which is why this also works for a
1039
+ * verification that never got as far as the selfie.
1040
+ */
1041
+ reanalyze(verificationId: string, options?: {
1042
+ signal?: AbortSignal;
1043
+ }): Promise<UploadResult>;
1044
+ /** The stored session, or null when there is none. */
1045
+ getSession(): Promise<StoredSession | null>;
1046
+ /**
1047
+ * Forgets the stored session. Call it once a verification is finished, so
1048
+ * the next one starts clean.
1049
+ */
1050
+ clearSession(): Promise<void>;
136
1051
  /**
137
- * Creates a new KYC Core instance
138
- * @param credentials API credentials (apiKey and baseUrl)
1052
+ * What the end user has uploaded and what is still missing.
1053
+ *
1054
+ * Derived from the files that actually exist server-side rather than from
1055
+ * anything the client remembers, so it is correct after a reinstall, on a
1056
+ * second device, or when a previous attempt failed mid-upload.
139
1057
  */
140
- constructor(credentials: KYCCredentials);
1058
+ getProgress(verificationId?: string, options?: ProgressOptions): Promise<VerificationProgress>;
141
1059
  /**
142
- * Registers a callback for KYC status events
143
- * @param callback Function to call when status changes
144
- * @returns Unsubscribe function to remove the callback
1060
+ * Uploads one step, skipping it when the file is already there.
1061
+ *
1062
+ * The call a resumable UI should make: it never re-sends a step the end user
1063
+ * already completed, which matters most on a retry after a dropped
1064
+ * connection, where the upload may well have succeeded.
145
1065
  */
146
- onEvent(callback: KYCEventCallback): () => void;
1066
+ uploadStep(params: UploadStepParams): Promise<UploadStepResult>;
147
1067
  /**
148
- * Emits a status event to all registered callbacks
149
- * @param event The event to emit
1068
+ * Polls the status until the verification is decided.
1069
+ *
1070
+ * Stops at APPROVED, REJECTED and — unless `stopOnReview` is false — REVIEW.
1071
+ * For anything longer than a user is willing to watch a spinner, use the
1072
+ * webhooks: polling burns a request every few seconds and still gives up
1073
+ * after `timeoutMs`.
150
1074
  */
151
- private emitEvent;
1075
+ waitForResult(verificationId?: string, options?: WaitForResultOptions): Promise<VerificationStatusResponse>;
1076
+ private resolveVerificationId;
152
1077
  /**
153
- * Creates a verification token for secure API access
154
- * @param externalId External reference ID
155
- * @param expiry Token expiry in hours (default: 3)
156
- * @returns Object containing the token and its expiration time
1078
+ * A page of the project's verifications, newest first.
1079
+ *
1080
+ * Carries no OCR data or storage paths by design — a 20-row page is not the
1081
+ * place for identity-document PII. Read one verification for those.
157
1082
  */
158
- createVerificationToken(externalId: string, expiry?: string): Promise<{
159
- token: string;
160
- expiresIn: string;
1083
+ list(params?: ListVerificationsParams, options?: {
1084
+ signal?: AbortSignal;
1085
+ }): Promise<Page<VerificationListItem>>;
1086
+ /** Counts per status and outcome, plus what needs an operator's attention. */
1087
+ summary(options?: {
1088
+ signal?: AbortSignal;
1089
+ }): Promise<VerificationSummary>;
1090
+ /**
1091
+ * A verification's recorded history, oldest first.
1092
+ *
1093
+ * The verification row keeps only the last decision, so this is the only
1094
+ * place the sequence survives.
1095
+ */
1096
+ events(verificationId: string, options?: {
1097
+ signal?: AbortSignal;
1098
+ }): Promise<PipelineEvent[]>;
1099
+ /** Moves a PENDING verification to REVIEW so a person can look at it. */
1100
+ sendToReview(verificationId: string, options?: {
1101
+ signal?: AbortSignal;
1102
+ }): Promise<{
1103
+ verificationId: string;
1104
+ status: string;
1105
+ message?: string;
161
1106
  }>;
162
1107
  /**
163
- * Starts a new verification session
164
- * @param params Parameters including documentType, country, identityId, and externalId
165
- * @returns Verification session with verificationId and status
1108
+ * Records a reviewer's decision and fires the matching webhook.
1109
+ *
1110
+ * Repeating the decision a verification already has is refused with 409 —
1111
+ * that is the gateway making a second webhook impossible, not a transient
1112
+ * failure. Changing a decision (rejected in error, then approved) is allowed.
166
1113
  */
167
- startVerification(params: StartVerificationParams): Promise<VerificationSession>;
1114
+ decide(verificationId: string, decision: ReviewDecision, options?: {
1115
+ signal?: AbortSignal;
1116
+ }): Promise<{
1117
+ verificationId: string;
1118
+ status: string;
1119
+ }>;
1120
+ }
1121
+
1122
+ /**
1123
+ * Where the gateway sends verification events.
1124
+ *
1125
+ * Webhooks are the right way to learn a verification's outcome: polling costs
1126
+ * a request every few seconds and still gives up, while a REVIEW can sit with
1127
+ * a human for hours. Subscribe with `["*"]` when the intent is "mirror my
1128
+ * verification lifecycle" — a literal list of today's events silently stops
1129
+ * being complete the day an eighth one exists.
1130
+ *
1131
+ * The URL must be https with a public hostname; the gateway refuses anything
1132
+ * else, localhost included.
1133
+ */
1134
+ declare class WebhooksResource {
1135
+ private readonly http;
1136
+ constructor(http: HttpClient);
1137
+ create(params: CreateWebhookParams, options?: {
1138
+ signal?: AbortSignal;
1139
+ }): Promise<Webhook>;
1140
+ list(options?: {
1141
+ signal?: AbortSignal;
1142
+ }): Promise<Webhook[]>;
1143
+ get(id: string, options?: {
1144
+ signal?: AbortSignal;
1145
+ }): Promise<Webhook>;
1146
+ update(id: string, params: UpdateWebhookParams, options?: {
1147
+ signal?: AbortSignal;
1148
+ }): Promise<Webhook>;
1149
+ delete(id: string, options?: {
1150
+ signal?: AbortSignal;
1151
+ }): Promise<void>;
168
1152
  /**
169
- * Uploads a selfie image for face verification
170
- * @param params Parameters including verificationId, imageData, and optional mimeType
171
- * @returns Upload result with status
1153
+ * Delivery attempts, newest first — what was sent, what came back, how many
1154
+ * retries it took. The place to look when an endpoint stops receiving events.
172
1155
  */
173
- uploadSelfie(params: UploadSelfieParams): Promise<UploadResult>;
1156
+ logs(params?: {
1157
+ page?: number;
1158
+ limit?: number;
1159
+ }, options?: {
1160
+ signal?: AbortSignal;
1161
+ }): Promise<Page<WebhookLogEntry>>;
1162
+ /** One delivery attempt. */
1163
+ log(id: string, options?: {
1164
+ signal?: AbortSignal;
1165
+ }): Promise<WebhookLogEntry>;
174
1166
  /**
175
- * Uploads a document image (front or back)
176
- * @param params Parameters including verificationId, type, imageData, and optional mimeType
177
- * @returns Upload result with status
1167
+ * Sends a recorded delivery again, to the endpoint it originally went to.
1168
+ *
1169
+ * Synchronous: the result says whether it arrived this time. A failure comes
1170
+ * back as `{ success: false, error }` rather than throwing, because the
1171
+ * gateway has already written a FAILED row either way and the caller usually
1172
+ * wants to show the reason beside the delivery it belongs to. A missing
1173
+ * delivery (404) or a deleted endpoint (409) still throw -- those are not
1174
+ * outcomes of the send.
1175
+ *
1176
+ * Not retried: a resend that timed out may well have arrived.
178
1177
  */
179
- uploadDocument(params: UploadDocumentParams): Promise<UploadResult>;
1178
+ resendLog(id: string, options?: {
1179
+ signal?: AbortSignal;
1180
+ }): Promise<WebhookResendResult>;
1181
+ }
1182
+
1183
+ interface KycirisClientConfig extends HttpClientConfig {
180
1184
  /**
181
- * Gets the current status of a verification session
182
- * @param verificationId The verification ID to check
183
- * @returns Current verification status including OCR data and face match score
1185
+ * Where to remember an in-flight verification, so a flow interrupted by the
1186
+ * app closing can be resumed. AsyncStorage on React Native, an adapter around
1187
+ * localStorage on web. No credential is ever written to it.
184
1188
  */
185
- getStatus(verificationId: string): Promise<VerificationStatus>;
186
- faceMatchVerification(params: FaceMatchVerificationParams): Promise<any>;
187
- faceMatchStatus({ externalId, faceCheckId, interval, timeout, }: FaceMatchStatusParams): Promise<VerificationStatus>;
1189
+ storage?: KycirisStorage;
1190
+ }
1191
+ /**
1192
+ * The KYCiris API client.
1193
+ *
1194
+ * Two credentials, two audiences:
1195
+ *
1196
+ * - **`apiKey`** is the project's key. It reaches every endpoint, including
1197
+ * every identity in the project, and belongs on your server only.
1198
+ * - **`verificationToken`** is scoped to one end user and authorizes exactly
1199
+ * the verification flow. Mint it on your server with
1200
+ * `verifications.createToken(externalId)` and hand it to the client app.
1201
+ *
1202
+ * Constructing a client with `apiKey` inside a browser or React Native app
1203
+ * throws, because a key in a shipped bundle is a key the end user has.
1204
+ *
1205
+ * ```ts
1206
+ * // Your server
1207
+ * const server = createKycirisClient({ baseUrl, apiKey: process.env.KYCIRIS_API_KEY });
1208
+ * const { token } = await server.verifications.createToken('user-123');
1209
+ *
1210
+ * // Your app, with that token
1211
+ * const client = createKycirisClient({ baseUrl, verificationToken: token, storage });
1212
+ * const { verificationId } = await client.verifications.start({
1213
+ * country: 'AO',
1214
+ * documentType: 'ID_CARD',
1215
+ * });
1216
+ * ```
1217
+ */
1218
+ declare class KycirisClient {
1219
+ /** The end-user flow, plus review and operations reads. */
1220
+ readonly verifications: VerificationsResource;
1221
+ /** Identity records and re-authentication face checks. API key only. */
1222
+ readonly identities: IdentitiesResource;
1223
+ /** Verification levels (step policies). API key only. */
1224
+ readonly levels: LevelsResource;
1225
+ /** Single-use downloads of verification images. API key only. */
1226
+ readonly media: MediaResourceApi;
1227
+ /** Standalone document reading, outside the verification flow. */
1228
+ readonly ocr: OcrResource;
1229
+ /** Event subscriptions and their delivery log. API key only. */
1230
+ readonly webhooks: WebhooksResource;
1231
+ /** Monthly counts. API key only. */
1232
+ readonly analytics: AnalyticsResource;
1233
+ private readonly http;
1234
+ constructor(config: KycirisClientConfig);
188
1235
  /**
189
- * Polls for verification status until completion or timeout
190
- * @param verificationId The verification ID to check
191
- * @param interval Polling interval in milliseconds (default: 3000)
192
- * @param timeout Maximum time to wait in milliseconds (default: 120000)
193
- * @returns Final verification status when approved or rejected
1236
+ * Whether this client holds the project API key, and so can reach the
1237
+ * server-side endpoints.
1238
+ *
1239
+ * Worth checking in code that runs in both places, rather than letting a
1240
+ * MISSING_CREDENTIAL error be the way you find out.
194
1241
  */
195
- pollStatus(verificationId: string, interval?: number, timeout?: number): Promise<VerificationStatus>;
1242
+ get isServerSide(): boolean;
1243
+ }
1244
+ /** Creates a KYCiris client. See {@link KycirisClient}. */
1245
+ declare function createKycirisClient(config: KycirisClientConfig): KycirisClient;
1246
+
1247
+ /**
1248
+ * Every error the SDK throws is a KycirisError, so a caller can branch on
1249
+ * `code` instead of parsing messages.
1250
+ *
1251
+ * Codes come from two places. Gateway codes are the `error.code` value in the
1252
+ * API's error envelope (`{ error: { code, message, details }, meta }`) and are
1253
+ * kept in step with the gateway's own ERROR_CODE enum. SDK codes are raised
1254
+ * client-side, before or instead of a request.
1255
+ */
1256
+ /**
1257
+ * Error codes produced by the gateway itself.
1258
+ *
1259
+ * Mirrors `ERROR_CODE` in kyc-gateway (src/common/enums/error-codes.ts). Codes
1260
+ * the SDK does not recognise are still surfaced verbatim on `KycirisError.code`
1261
+ * — the list is for autocomplete and exhaustiveness, not for filtering.
1262
+ */
1263
+ declare const GATEWAY_ERROR_CODES: readonly ["INVALID_OCR_DATA", "OCR_EXTRACTION_ERROR", "DOCUMENT_EXPIRED", "INVALID_DOCUMENT_QUALITY", "FACE_MISMATCH", "LOW_FACE_MATCH_SCORE", "NO_FACE", "MANUAL_REJECTION", "BAD_PAYLOAD", "UNSUPPORTED_DOCUMENT", "BAD_IMAGE", "ACCOUNT_SUSPENDED", "VALIDATION_ERROR", "UNAUTHORIZED", "FORBIDDEN", "NOT_FOUND", "RESOURCE_ALREADY_EXISTS", "RATE_LIMIT_EXCEEDED", "INTERNAL_SERVER_ERROR"];
1264
+ type GatewayErrorCode = (typeof GATEWAY_ERROR_CODES)[number];
1265
+ /** Error codes raised by the SDK without (or before) a gateway response. */
1266
+ declare const SDK_ERROR_CODES: readonly ["CONFIG_ERROR", "CREDENTIAL_MISUSE", "MISSING_CREDENTIAL", "NETWORK_ERROR", "TIMEOUT", "ABORTED", "INVALID_RESPONSE", "INVALID_FILE", "POLL_TIMEOUT", "SESSION_NOT_FOUND"];
1267
+ type SdkErrorCode = (typeof SDK_ERROR_CODES)[number];
1268
+ /** Any code that can land on a KycirisError. Open-ended by design. */
1269
+ type KycirisErrorCode = GatewayErrorCode | SdkErrorCode | (string & {});
1270
+ interface KycirisErrorInit {
1271
+ code: KycirisErrorCode;
1272
+ /** HTTP status, when the error came from a response. */
1273
+ status?: number;
1274
+ /** `meta.requestId` from the response envelope — quote it in bug reports. */
1275
+ requestId?: string;
1276
+ /** `error.details` from the envelope, e.g. per-field validation failures. */
1277
+ details?: unknown;
1278
+ /** Whether retrying the same request could plausibly succeed. */
1279
+ retryable?: boolean;
1280
+ /** The underlying error, when this one wraps a lower-level failure. */
1281
+ cause?: unknown;
1282
+ }
1283
+ /**
1284
+ * The single error type the SDK throws.
1285
+ *
1286
+ * It deliberately carries no request/response objects: those hold the
1287
+ * Authorization and X-API-Key headers, and an error is the thing most likely to
1288
+ * be logged verbatim or shipped to an error tracker.
1289
+ */
1290
+ declare class KycirisError extends Error {
1291
+ readonly name = "KycirisError";
1292
+ readonly code: KycirisErrorCode;
1293
+ readonly status?: number;
1294
+ readonly requestId?: string;
1295
+ readonly details?: unknown;
1296
+ readonly retryable: boolean;
1297
+ constructor(message: string, init: KycirisErrorInit);
1298
+ /** True when this is a KycirisError, across bundle/realm boundaries. */
1299
+ static isKycirisError(value: unknown): value is KycirisError;
1300
+ }
1301
+ /** Convenience guard, equivalent to `KycirisError.isKycirisError`. */
1302
+ declare function isKycirisError(value: unknown): value is KycirisError;
1303
+ /**
1304
+ * HTTP statuses worth retrying: the request either never ran (429) or failed
1305
+ * for a reason that is plausibly momentary.
1306
+ */
1307
+ declare function isRetryableStatus(status: number): boolean;
1308
+
1309
+ /** Where the end user is in the flow. */
1310
+ type FlowPhase =
1311
+ /** Nothing started yet. */
1312
+ 'idle'
1313
+ /** Creating the verification. */
1314
+ | 'starting'
1315
+ /** Waiting for the user to supply the current step's image. */
1316
+ | 'collecting'
1317
+ /** Uploading an image. */
1318
+ | 'uploading'
1319
+ /** Everything submitted; the pipeline is deciding. */
1320
+ | 'processing'
1321
+ /** A decision was reached, or a human was asked for one. */
1322
+ | 'finished'
1323
+ /** The flow stopped on an error. `error` says which. */
1324
+ | 'failed';
1325
+ interface FlowState {
1326
+ phase: FlowPhase;
1327
+ verificationId: string | null;
1328
+ /** The step to ask the user for now, or null when none is outstanding. */
1329
+ currentStep: VerificationStep | null;
1330
+ /** Every step still to collect, in order. */
1331
+ missingSteps: VerificationStep[];
1332
+ uploaded: Record<VerificationStep, boolean>;
1333
+ status: VerificationStatus | null;
1334
+ outcome: VerificationOutcome | null;
1335
+ /** Why the pipeline stopped, when it did. */
1336
+ rejectionReason: string | null;
1337
+ error: KycirisError | null;
1338
+ /** True while a request is in flight, for disabling a capture button. */
1339
+ busy: boolean;
1340
+ }
1341
+ interface FlowOptions {
1342
+ country: CatalogCountry | (string & {});
1343
+ documentType: DocumentType | (string & {});
1344
+ /** Verification level to run under. Required: the gateway has no default. */
1345
+ levelName: string;
1346
+ /** Your reference for the user. Ignored when a verification token is used. */
1347
+ externalId?: string;
196
1348
  /**
197
- * Static factory method to create a KYC client instance
198
- * @param credentials API credentials (apiKey and baseUrl)
199
- * @returns Configured KYCCore instance
1349
+ * Whether the level asks for a selfie. Defaults to true.
1350
+ *
1351
+ * A client holding a verification token cannot read its own level, so a
1352
+ * document-only level has to be declared here.
200
1353
  */
201
- static createClient(credentials: KYCCredentials): KYCCore;
1354
+ selfieRequired?: boolean;
1355
+ /** Poll for the decision once everything is uploaded. Defaults to true. */
1356
+ waitForDecision?: boolean;
1357
+ /** How long to poll before giving up, in ms. Defaults to 120000. */
1358
+ decisionTimeoutMs?: number;
1359
+ /** Called on every state change. */
1360
+ onStateChange?: (state: FlowState) => void;
202
1361
  }
203
1362
  /**
204
- * Factory function to create a KYC client instance
205
- * @param credentials API credentials (apiKey and baseUrl)
206
- * @returns Configured KYCCore instance
1363
+ * The end-user verification flow as a state machine, with no UI attached.
1364
+ *
1365
+ * The React Native and web components are both thin wrappers around this, so
1366
+ * the sequencing — which step comes next, what a resumed flow skips, when the
1367
+ * decision is polled for — is written and tested once.
1368
+ *
1369
+ * Every transition is observable through `subscribe`, and every method is safe
1370
+ * to call from a button handler: failures land in `state.error` rather than
1371
+ * rejecting, so a component never has to wrap calls in try/catch.
1372
+ *
1373
+ * ```ts
1374
+ * const flow = new VerificationFlow(client, { country: 'AO', documentType: 'ID_CARD' });
1375
+ * flow.subscribe(render);
1376
+ * await flow.start();
1377
+ * // then, per captured image:
1378
+ * await flow.submit(image);
1379
+ * ```
207
1380
  */
208
- declare function createKYCClient(credentials: KYCCredentials): KYCCore;
1381
+ declare class VerificationFlow {
1382
+ private readonly client;
1383
+ private readonly options;
1384
+ private state;
1385
+ private listeners;
1386
+ private aborter;
1387
+ constructor(client: KycirisClient, options: FlowOptions);
1388
+ getState(): FlowState;
1389
+ /** Observes state changes. Returns an unsubscribe function. */
1390
+ subscribe(listener: (state: FlowState) => void): () => void;
1391
+ /**
1392
+ * Starts a verification, or picks up the one already in storage.
1393
+ *
1394
+ * Resuming is the default because it is almost always what the user wants:
1395
+ * an app killed between the document and the selfie should carry on, not
1396
+ * ask for the document again. Pass `{ fresh: true }` to start over.
1397
+ */
1398
+ start(options?: {
1399
+ fresh?: boolean;
1400
+ }): Promise<FlowState>;
1401
+ /**
1402
+ * Re-reads what the gateway holds.
1403
+ *
1404
+ * Worth calling when a component remounts, or after an upload whose response
1405
+ * never arrived: the file may well have landed, and this is what notices.
1406
+ */
1407
+ refresh(): Promise<FlowState>;
1408
+ /**
1409
+ * Uploads an image for the current step and advances.
1410
+ *
1411
+ * A step that turns out to be already uploaded is skipped rather than sent
1412
+ * again, so retrying after a dropped connection cannot duplicate work.
1413
+ */
1414
+ submit(file: FileInput): Promise<FlowState>;
1415
+ /**
1416
+ * Waits for the pipeline's decision.
1417
+ *
1418
+ * Called automatically once the last step is uploaded, unless
1419
+ * `waitForDecision` is false. Stops at REVIEW, which is a person's queue and
1420
+ * not something worth holding a spinner for.
1421
+ */
1422
+ waitForDecision(): Promise<FlowState>;
1423
+ /**
1424
+ * Cancels whatever is in flight.
1425
+ *
1426
+ * A component's unmount should call this, so a poll does not keep running
1427
+ * against a screen that is gone.
1428
+ */
1429
+ cancel(): void;
1430
+ /** Cancels, forgets the stored session, and returns to `idle`. */
1431
+ reset(): Promise<FlowState>;
1432
+ /**
1433
+ * Reads progress and derives the next phase from it.
1434
+ *
1435
+ * Returns a patch rather than applying one, so `guard` stays the single
1436
+ * place that publishes a state change.
1437
+ */
1438
+ private loadProgress;
1439
+ /**
1440
+ * Runs one transition: marks the flow busy, applies the resulting patch, and
1441
+ * turns any failure into `state.error` instead of a rejected promise.
1442
+ *
1443
+ * The awkward part it exists to contain is that reaching `processing` should
1444
+ * flow straight on into polling — but only when the caller asked for it, and
1445
+ * only once.
1446
+ */
1447
+ private guard;
1448
+ private fail;
1449
+ private patch;
1450
+ }
209
1451
 
210
- export { type FaceMatchStatusParams, type FaceMatchVerificationParams, KYCCore, type KYCCredentials, type KYCEventCallback, KYCSdkError, type KYCStatusEvent, type StartVerificationParams, type UploadDocumentParams, type UploadResult, type UploadSelfieParams, type VerificationSession, type VerificationStatus, createKYCClient };
1452
+ export { ALL_WEBHOOK_EVENTS, type Analytics, AnalyticsResource, type AnalyticsSeries, type ApiMeta, type ApiResult, type AuthMode, type AvailableMedia, type CatalogCountry, type CreateLevelParams, type CreateWebhookParams, DOCUMENT_CATALOG, DUPLICATE_FIELDS, type DocumentCatalogEntry, type DocumentSide, type DocumentType, type DownloadToken, type DuplicateField, type DuplicateGroup, type DuplicatesByField, type DuplicatesByFields, type FaceCheck, type FetchLike, type FileInput, type FlowOptions, type FlowPhase, type FlowState, GATEWAY_ERROR_CODES, type GatewayErrorCode, HttpClient, type HttpClientConfig, IdentitiesResource, type Identity, type IdentityDocumentStep, type IdentityFields, type IdentitySummary, type ImageMimeType, KycirisClient, type KycirisClientConfig, KycirisError, type KycirisErrorCode, type KycirisErrorInit, type KycirisStorage, LevelsResource, type ListIdentitiesParams, type ListVerificationsParams, MAX_IMAGE_FILE_SIZE, type MediaResource, MediaResourceApi, type NormalizedFile, type OcrData, type OcrProcess, type OcrProcessStatus, type OcrProcessStatusResponse, OcrResource, PIPELINE_EVENT_KINDS, type Page, type PaginationMeta, type PipelineEvent, type PipelineEventKind, type PollCallbacks, type PollOptions, type ProgressOptions, type RequestOptions, type ReviewDecision, type RunOcrParams, SDK_ERROR_CODES, SESSION_STORAGE_KEY, type SdkErrorCode, type SelfieStep, SessionStore, type StartVerificationParams, type StoredSession, TERMINAL_VERIFICATION_STATUSES, type UpdateLevelParams, type UpdateWebhookParams, type UploadOptions, type UploadResult, type UploadStepParams, type UploadStepResult, VERIFICATION_OUTCOMES, VERIFICATION_STATUSES, VerificationFlow, type VerificationLevel, type VerificationLevelDefinition, type VerificationLevelStep, type VerificationListItem, type VerificationOutcome, type VerificationProgress, type VerificationSession, type VerificationStatus, type VerificationStatusResponse, type VerificationStep, type VerificationSummary, type VerificationToken, type VerificationTokenProvider, VerificationsResource, WEBHOOK_EVENT_TYPES, type WaitForResultOptions, type Webhook, type WebhookEventType, type WebhookLogEntry, type WebhookResendResult, type WebhookSubscription, WebhooksResource, createKycirisClient, createMemoryStorage, decodeBase64, detectImageMimeType, encodeBase64, isKycirisError, isNormalizedFile, isReactNative, isRetryableStatus, isSupportedDocumentPair, isTerminalStatus, isUntrustedClient, normalizeFile, pollUntil, prepareFile, supportedCountries, supportedDocumentTypes, toPage };