@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/README.md +273 -160
- package/dist/index.d.mts +1395 -153
- package/dist/index.d.ts +1395 -153
- package/dist/index.js +1930 -256
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1891 -252
- package/dist/index.mjs.map +1 -1
- package/package.json +39 -12
package/dist/index.d.mts
CHANGED
|
@@ -1,210 +1,1452 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
60
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/**
|
|
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
|
-
|
|
74
|
-
status: string;
|
|
366
|
+
status: VerificationStatus;
|
|
75
367
|
}
|
|
76
|
-
/**
|
|
77
|
-
interface
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
*
|
|
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
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
|
|
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
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
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
|
-
|
|
966
|
+
stopOnReview?: boolean;
|
|
128
967
|
}
|
|
129
968
|
/**
|
|
130
|
-
*
|
|
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
|
|
133
|
-
private
|
|
134
|
-
private
|
|
135
|
-
|
|
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
|
-
*
|
|
138
|
-
*
|
|
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
|
-
|
|
1058
|
+
getProgress(verificationId?: string, options?: ProgressOptions): Promise<VerificationProgress>;
|
|
141
1059
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
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
|
-
|
|
1066
|
+
uploadStep(params: UploadStepParams): Promise<UploadStepResult>;
|
|
147
1067
|
/**
|
|
148
|
-
*
|
|
149
|
-
*
|
|
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
|
-
|
|
1075
|
+
waitForResult(verificationId?: string, options?: WaitForResultOptions): Promise<VerificationStatusResponse>;
|
|
1076
|
+
private resolveVerificationId;
|
|
152
1077
|
/**
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
170
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
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
|
-
|
|
1178
|
+
resendLog(id: string, options?: {
|
|
1179
|
+
signal?: AbortSignal;
|
|
1180
|
+
}): Promise<WebhookResendResult>;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
interface KycirisClientConfig extends HttpClientConfig {
|
|
180
1184
|
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
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
|
|
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
|
|
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 };
|