@certysign/sdk 2.3.0 → 2.5.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.
@@ -53,6 +53,8 @@ class HashSigningResource {
53
53
  reason = 'Digital signature',
54
54
  location = 'Nairobi, Kenya',
55
55
  hashAlgorithm = 'sha256',
56
+ signerName,
57
+ signerEmail,
56
58
  metadata = {}
57
59
  } = options;
58
60
 
@@ -69,6 +71,8 @@ class HashSigningResource {
69
71
  fileName,
70
72
  reason,
71
73
  location,
74
+ signerName,
75
+ signerEmail,
72
76
  metadata
73
77
  }
74
78
  });
@@ -99,13 +103,22 @@ class HashSigningResource {
99
103
  fileName,
100
104
  reason = 'Digital signature',
101
105
  location = 'Nairobi, Kenya',
102
- metadata = {}
106
+ signerName,
107
+ signerEmail,
108
+ metadata = {},
109
+ // Second factor. Single-document signing challenges the named signer: the
110
+ // first call returns 2FA_REQUIRED with a verificationNonce and sends them a
111
+ // code; re-send the same request carrying both. Batch signing is exempt.
112
+ twoFactorCode,
113
+ twoFactorMethod,
114
+ verificationNonce
103
115
  } = options;
104
116
 
105
117
  if (!documentHash) throw new Error('signHash: documentHash is required');
106
118
 
107
119
  return this._http.post('/sdk/v1/sign/hash', {
108
- data: { documentHash, hashAlgorithm, fileName, reason, location, metadata }
120
+ data: { documentHash, hashAlgorithm, fileName, reason, location, signerName, signerEmail, metadata,
121
+ twoFactorCode, twoFactorMethod, verificationNonce }
109
122
  });
110
123
  }
111
124
 
@@ -132,6 +145,8 @@ class HashSigningResource {
132
145
  documents,
133
146
  reason = 'Batch digital signature',
134
147
  hashAlgorithm = 'sha256',
148
+ signerName,
149
+ signerEmail,
135
150
  metadata = {}
136
151
  } = options;
137
152
 
@@ -148,7 +163,7 @@ class HashSigningResource {
148
163
  });
149
164
 
150
165
  return this._http.post('/sdk/v1/sign/hash/batch', {
151
- data: { documents: hashed, reason, metadata }
166
+ data: { documents: hashed, reason, signerName, signerEmail, metadata }
152
167
  });
153
168
  }
154
169
 
@@ -159,12 +174,12 @@ class HashSigningResource {
159
174
  * @returns {Promise<BatchHashSignResult>}
160
175
  */
161
176
  async batchSignHashes(options) {
162
- const { documents, reason = 'Batch digital signature', metadata = {} } = options;
177
+ const { documents, reason = 'Batch digital signature', signerName, signerEmail, metadata = {} } = options;
163
178
 
164
179
  if (!documents?.length) throw new Error('batchSignHashes: documents array is required');
165
180
 
166
181
  return this._http.post('/sdk/v1/sign/hash/batch', {
167
- data: { documents, reason, metadata }
182
+ data: { documents, reason, signerName, signerEmail, metadata }
168
183
  });
169
184
  }
170
185
 
@@ -1,198 +1,211 @@
1
- /**
2
- * @fileoverview CertySign SDK — HTTP client layer
3
- *
4
- * Wraps axios with:
5
- * - Automatic API key header injection
6
- * - Idempotency key support
7
- * - Retry with exponential back-off (network errors, 429, 503)
8
- * - Consistent error normalisation
9
- */
10
-
11
- 'use strict';
12
-
13
- const axios = require('axios');
14
- const FormData = require('form-data');
15
- const crypto = require('crypto');
16
-
17
- /** Default base URL — override per environment */
18
- const DEFAULT_BASE_URL = 'https://core.certysign.io';
19
-
20
- /** Statuses that should trigger a retry */
21
- const RETRYABLE_STATUS = new Set([429, 502, 503, 504]);
22
-
23
- /**
24
- * @typedef {Object} HttpClientOptions
25
- * @property {string} publicKey - API public key (cs_pk_...)
26
- * @property {string} secretKey - API secret key (cs_sk_...)
27
- * @property {string} [baseUrl] - Override default gateway URL
28
- * @property {number} [timeout] - Request timeout ms (default 30000)
29
- * @property {number} [retries] - Max retries on transient errors (default 3)
30
- * @property {boolean} [debug] - Log requests/responses
31
- */
32
-
33
- class HttpClient {
34
- /**
35
- * @param {HttpClientOptions} options
36
- */
37
- constructor(options) {
38
- if (!options.publicKey) throw new Error('CertySign SDK: publicKey is required');
39
- if (!options.secretKey) throw new Error('CertySign SDK: secretKey is required');
40
-
41
- this.publicKey = options.publicKey;
42
- this.secretKey = options.secretKey;
43
- this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, '');
44
- this.timeout = options.timeout ?? 30_000;
45
- this.retries = options.retries ?? 3;
46
- this.debug = options.debug ?? false;
47
-
48
- this._client = axios.create({
49
- baseURL: this.baseUrl,
50
- timeout: this.timeout,
51
- headers: {
52
- 'Content-Type': 'application/json',
53
- 'Accept': 'application/json',
54
- 'User-Agent': `CertySign-SDK-Node/1.0.0 Node/${process.version}`
55
- }
56
- });
57
- }
58
-
59
- // ── Core request execution ───────────────────────────────────────────────
60
-
61
- /**
62
- * Execute an HTTP request with retry logic.
63
- *
64
- * @param {string} method - HTTP verb (GET, POST, PUT, DELETE, PATCH)
65
- * @param {string} path - Path relative to baseUrl
66
- * @param {Object} [options]
67
- * @param {Object} [options.data] - JSON body
68
- * @param {Object} [options.params] - Query params
69
- * @param {Object} [options.headers] - Extra headers
70
- * @param {FormData} [options.formData] - Multipart form (takes precedence over data)
71
- * @param {string} [options.idempotencyKey] - Idempotency key (auto-generated if omitted)
72
- * @param {string} [options.accept] - Override Accept header
73
- * @param {'json'|'arraybuffer'|'text'} [options.responseType] - Expected response type
74
- * @returns {Promise<any>}
75
- */
76
- async request(method, path, options = {}) {
77
- const {
78
- data,
79
- params,
80
- headers: extraHeaders = {},
81
- formData,
82
- idempotencyKey,
83
- accept,
84
- responseType = 'json'
85
- } = options;
86
-
87
- const idempKey = idempotencyKey || `sdk_${crypto.randomBytes(16).toString('hex')}`;
88
-
89
- const headers = {
90
- 'X-API-Key-Id': this.publicKey,
91
- 'X-API-Key-Secret': this.secretKey,
92
- 'X-Idempotency-Key': idempKey,
93
- ...extraHeaders
94
- };
95
-
96
- if (accept) headers['Accept'] = accept;
97
-
98
- const config = {
99
- method,
100
- url: path,
101
- headers,
102
- params,
103
- responseType
104
- };
105
-
106
- if (formData) {
107
- config.data = formData;
108
- config.headers = { ...config.headers, ...formData.getHeaders() };
109
- delete config.headers['Content-Type']; // Let axios set multipart boundary
110
- } else if (data !== undefined) {
111
- config.data = data;
112
- }
113
-
114
- let attempt = 0;
115
-
116
- while (true) { // eslint-disable-line no-constant-condition
117
- attempt++;
118
-
119
- if (this.debug) {
120
- console.debug(`[CertySign SDK] ${method.toUpperCase()} ${path} (attempt ${attempt})`);
121
- }
122
-
123
- try {
124
- const response = await this._client.request(config);
125
- return response.data;
126
-
127
- } catch (err) {
128
- const status = err.response?.status;
129
- const canRetry = RETRYABLE_STATUS.has(status) || !status; // network error
130
-
131
- if (canRetry && attempt <= this.retries) {
132
- const delay = Math.min(200 * 2 ** (attempt - 1), 4000);
133
- if (this.debug) console.debug(`[CertySign SDK] Retrying in ${delay}ms (status=${status})`);
134
- await _sleep(delay);
135
- continue;
136
- }
137
-
138
- throw _normaliseError(err);
139
- }
140
- }
141
- }
142
-
143
- get(path, options) { return this.request('GET', path, options); }
144
- post(path, options) { return this.request('POST', path, options); }
145
- put(path, options) { return this.request('PUT', path, options); }
146
- patch(path, options) { return this.request('PATCH', path, options); }
147
- delete(path, options) { return this.request('DELETE', path, options); }
148
- }
149
-
150
- // ── Helpers ──────────────────────────────────────────────────────────────────
151
-
152
- function _sleep(ms) {
153
- return new Promise(resolve => setTimeout(resolve, ms));
154
- }
155
-
156
- /**
157
- * Normalise an axios error into a plain CertySignError.
158
- * @param {Error} err
159
- * @returns {CertySignError}
160
- */
161
- function _normaliseError(err) {
162
- if (err.response) {
163
- const body = err.response.data;
164
- const e = new CertySignError(
165
- body?.message || `HTTP ${err.response.status}`,
166
- err.response.status,
167
- body?.code
168
- );
169
- e.details = body;
170
- e.headers = err.response.headers;
171
- return e;
172
- }
173
-
174
- if (err.request) {
175
- return new CertySignError(`No response received: ${err.message}`, 0, 'NETWORK_ERROR');
176
- }
177
-
178
- return new CertySignError(err.message, 0, 'SDK_INTERNAL_ERROR');
179
- }
180
-
181
- /**
182
- * Error class thrown by all SDK methods on failure.
183
- *
184
- * @property {number} statusCode - HTTP status code (0 for network errors)
185
- * @property {string} code - Machine-readable error code (e.g. 'INVALID_API_KEY')
186
- * @property {Object} details - Full error body from the API
187
- */
188
- class CertySignError extends Error {
189
- constructor(message, statusCode, code) {
190
- super(message);
191
- this.name = 'CertySignError';
192
- this.statusCode = statusCode;
193
- this.code = code;
194
- this.details = null;
195
- }
196
- }
197
-
198
- module.exports = { HttpClient, CertySignError };
1
+ /**
2
+ * @fileoverview CertySign SDK — HTTP client layer
3
+ *
4
+ * Wraps axios with:
5
+ * - Automatic API key header injection
6
+ * - Idempotency key support
7
+ * - Retry with exponential back-off (network errors, 429, 503)
8
+ * - Consistent error normalisation
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const axios = require('axios');
14
+ const FormData = require('form-data');
15
+ const crypto = require('crypto');
16
+
17
+ /** Default base URL — override per environment */
18
+ const DEFAULT_BASE_URL = 'https://core.certysign.io';
19
+
20
+ /** Statuses that should trigger a retry */
21
+ const RETRYABLE_STATUS = new Set([429, 502, 503, 504]);
22
+
23
+ /**
24
+ * @typedef {Object} HttpClientOptions
25
+ * @property {string} publicKey - API public key (cs_pk_...)
26
+ * @property {string} secretKey - API secret key (cs_sk_...)
27
+ * @property {string} [baseUrl] - Override default gateway URL
28
+ * @property {number} [timeout] - Request timeout ms (default 30000)
29
+ * @property {number} [retries] - Max retries on transient errors (default 3)
30
+ * @property {boolean} [debug] - Log requests/responses
31
+ */
32
+
33
+ class HttpClient {
34
+ /**
35
+ * @param {HttpClientOptions} options
36
+ */
37
+ constructor(options) {
38
+ if (!options.publicKey) throw new Error('CertySign SDK: publicKey is required');
39
+ if (!options.secretKey) throw new Error('CertySign SDK: secretKey is required');
40
+
41
+ this.publicKey = options.publicKey;
42
+ this.secretKey = options.secretKey;
43
+ this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, '');
44
+ this.timeout = options.timeout ?? 30_000;
45
+ this.retries = options.retries ?? 3;
46
+ this.debug = options.debug ?? false;
47
+
48
+ this._client = axios.create({
49
+ baseURL: this.baseUrl,
50
+ timeout: this.timeout,
51
+ headers: {
52
+ 'Content-Type': 'application/json',
53
+ 'Accept': 'application/json',
54
+ 'User-Agent': `CertySign-SDK-Node/1.0.0 Node/${process.version}`
55
+ }
56
+ });
57
+ }
58
+
59
+ // ── Core request execution ───────────────────────────────────────────────
60
+
61
+ /**
62
+ * Execute an HTTP request with retry logic.
63
+ *
64
+ * @param {string} method - HTTP verb (GET, POST, PUT, DELETE, PATCH)
65
+ * @param {string} path - Path relative to baseUrl
66
+ * @param {Object} [options]
67
+ * @param {Object} [options.data] - JSON body
68
+ * @param {Object} [options.params] - Query params
69
+ * @param {Object} [options.headers] - Extra headers
70
+ * @param {FormData} [options.formData] - Multipart form (takes precedence over data)
71
+ * @param {string} [options.idempotencyKey] - Idempotency key (auto-generated if omitted)
72
+ * @param {string} [options.accept] - Override Accept header
73
+ * @param {'json'|'arraybuffer'|'text'} [options.responseType] - Expected response type
74
+ * @returns {Promise<any>}
75
+ */
76
+ async request(method, path, options = {}) {
77
+ const {
78
+ data,
79
+ params,
80
+ headers: extraHeaders = {},
81
+ formData,
82
+ idempotencyKey,
83
+ accept,
84
+ responseType = 'json'
85
+ } = options;
86
+
87
+ const idempKey = idempotencyKey || `sdk_${crypto.randomBytes(16).toString('hex')}`;
88
+
89
+ const headers = {
90
+ 'X-API-Key-Id': this.publicKey,
91
+ 'X-API-Key-Secret': this.secretKey,
92
+ 'X-Idempotency-Key': idempKey,
93
+ ...extraHeaders
94
+ };
95
+
96
+ if (accept) headers['Accept'] = accept;
97
+
98
+ const config = {
99
+ method,
100
+ url: path,
101
+ headers,
102
+ params,
103
+ responseType
104
+ };
105
+
106
+ if (formData) {
107
+ config.data = formData;
108
+ config.headers = { ...config.headers, ...formData.getHeaders() };
109
+ delete config.headers['Content-Type']; // Let axios set multipart boundary
110
+ } else if (data !== undefined) {
111
+ config.data = data;
112
+ }
113
+
114
+ let attempt = 0;
115
+
116
+ while (true) { // eslint-disable-line no-constant-condition
117
+ attempt++;
118
+
119
+ if (this.debug) {
120
+ console.debug(`[CertySign SDK] ${method.toUpperCase()} ${path} (attempt ${attempt})`);
121
+ }
122
+
123
+ try {
124
+ const response = await this._client.request(config);
125
+ return response.data;
126
+
127
+ } catch (err) {
128
+ const status = err.response?.status;
129
+ const code = err.response?.data?.code;
130
+
131
+ // A 409 usually means "your request conflicts with reality" and retrying is
132
+ // pointless. IDEMPOTENCY_IN_PROGRESS is the exception: an identical request
133
+ // with this same key is mid-flight, and the right move is to wait for it
134
+ // rather than fail. Retrying is safe precisely BECAUSE the key is the same —
135
+ // the server will hand back that request's result, not run a second one.
136
+ const waitingOnTwin = status === 409 && code === 'IDEMPOTENCY_IN_PROGRESS';
137
+
138
+ const canRetry = RETRYABLE_STATUS.has(status) || waitingOnTwin || !status;
139
+
140
+ if (canRetry && attempt <= this.retries) {
141
+ // Honour Retry-After when the server states one, rather than guessing.
142
+ const retryAfter = Number(err.response?.headers?.['retry-after']);
143
+ const delay = Number.isFinite(retryAfter) && retryAfter > 0
144
+ ? Math.min(retryAfter * 1000, 10000)
145
+ : Math.min(200 * 2 ** (attempt - 1), 4000);
146
+ if (this.debug) console.debug(`[CertySign SDK] Retrying in ${delay}ms (status=${status})`);
147
+ await _sleep(delay);
148
+ continue;
149
+ }
150
+
151
+ throw _normaliseError(err);
152
+ }
153
+ }
154
+ }
155
+
156
+ get(path, options) { return this.request('GET', path, options); }
157
+ post(path, options) { return this.request('POST', path, options); }
158
+ put(path, options) { return this.request('PUT', path, options); }
159
+ patch(path, options) { return this.request('PATCH', path, options); }
160
+ delete(path, options) { return this.request('DELETE', path, options); }
161
+ }
162
+
163
+ // ── Helpers ──────────────────────────────────────────────────────────────────
164
+
165
+ function _sleep(ms) {
166
+ return new Promise(resolve => setTimeout(resolve, ms));
167
+ }
168
+
169
+ /**
170
+ * Normalise an axios error into a plain CertySignError.
171
+ * @param {Error} err
172
+ * @returns {CertySignError}
173
+ */
174
+ function _normaliseError(err) {
175
+ if (err.response) {
176
+ const body = err.response.data;
177
+ const e = new CertySignError(
178
+ body?.message || `HTTP ${err.response.status}`,
179
+ err.response.status,
180
+ body?.code
181
+ );
182
+ e.details = body;
183
+ e.headers = err.response.headers;
184
+ return e;
185
+ }
186
+
187
+ if (err.request) {
188
+ return new CertySignError(`No response received: ${err.message}`, 0, 'NETWORK_ERROR');
189
+ }
190
+
191
+ return new CertySignError(err.message, 0, 'SDK_INTERNAL_ERROR');
192
+ }
193
+
194
+ /**
195
+ * Error class thrown by all SDK methods on failure.
196
+ *
197
+ * @property {number} statusCode - HTTP status code (0 for network errors)
198
+ * @property {string} code - Machine-readable error code (e.g. 'INVALID_API_KEY')
199
+ * @property {Object} details - Full error body from the API
200
+ */
201
+ class CertySignError extends Error {
202
+ constructor(message, statusCode, code) {
203
+ super(message);
204
+ this.name = 'CertySignError';
205
+ this.statusCode = statusCode;
206
+ this.code = code;
207
+ this.details = null;
208
+ }
209
+ }
210
+
211
+ module.exports = { HttpClient, CertySignError };
@@ -0,0 +1,143 @@
1
+ /**
2
+ * @fileoverview IdentityResource — Identity-as-a-Service
3
+ *
4
+ * Verify a national ID against the issuing authority (IPRS for Kenya) and get
5
+ * the biographic record back — the same pipeline CertySign uses to onboard its
6
+ * own signers.
7
+ *
8
+ * Registry-first: an ID already verified anywhere on the CertySign network is
9
+ * served from our registry (`source: 'registry'`) with no authority call.
10
+ *
11
+ * Requires the `identity:verify` scope on your API key. That scope is NOT granted
12
+ * by default — enable it under Settings → Security → SDK API Keys → Permissions.
13
+ *
14
+ * PRIVACY: this endpoint processes another person's personal data. You must
15
+ * attest that the data subject consented; the attestation is recorded in your
16
+ * tenant's audit trail on every call. The full ID number is never echoed back —
17
+ * store the returned `identityReference` instead of the raw number.
18
+ */
19
+
20
+ 'use strict';
21
+
22
+ /** Countries / ID types the service can reach today. */
23
+ const SUPPORTED = Object.freeze({ KE: Object.freeze(['NATIONAL_ID']) });
24
+
25
+ class IdentityResource {
26
+ /** @param {import('./HttpClient').HttpClient} http */
27
+ constructor(http) {
28
+ this._http = http;
29
+ }
30
+
31
+ /**
32
+ * Verify a national ID against the issuing authority.
33
+ *
34
+ * @param {Object} params
35
+ * @param {string} params.country - ISO-3166 alpha-2, e.g. 'KE'
36
+ * @param {string} params.idNumber - The subject's national ID number
37
+ * @param {string} [params.idType] - Defaults to 'NATIONAL_ID'
38
+ * @param {Object} params.consent - Lawful-basis attestation (REQUIRED)
39
+ * @param {boolean} params.consent.obtained - Must be true
40
+ * @param {string} [params.consent.reference] - Your own record id for the consent
41
+ * @param {string} [params.consent.purpose] - Why you're verifying, e.g. 'KYC onboarding'
42
+ * @param {string} [params.firstName] - Optional cross-check hint
43
+ * @param {string} [params.lastName] - Optional cross-check hint
44
+ * @param {string} [params.dob] - Optional cross-check hint (YYYY-MM-DD)
45
+ * @param {boolean} [params.forceRefresh] - Skip the registry/negative cache and
46
+ * go straight to the authority
47
+ * @param {string} [params.idempotencyKey] - Sent as X-Idempotency-Key
48
+ *
49
+ * @returns {Promise<Object>} `{ success, data }` where data contains
50
+ * `verified`, `decision`, `identityReference`, `idLast4`, `person`,
51
+ * `source` ('registry' | 'authority'), `cached`, `resultCode`, `checkedAt`.
52
+ *
53
+ * @throws {CertySignError} 403 CONSENT_REQUIRED, 402 INSUFFICIENT_BALANCE,
54
+ * 422 COUNTRY_NOT_SUPPORTED / ID_TYPE_NOT_SUPPORTED, 502 VERIFICATION_UNAVAILABLE
55
+ *
56
+ * @example
57
+ * const { data } = await client.identity.verify({
58
+ * country: 'KE',
59
+ * idNumber: '12345678',
60
+ * consent: { obtained: true, reference: 'loan-8821', purpose: 'KYC onboarding' }
61
+ * });
62
+ *
63
+ * if (data.verified) {
64
+ * console.log('Verified:', data.person.fullName, data.person.dob);
65
+ * // Store this instead of the raw ID number:
66
+ * await db.users.update(userId, { identityRef: data.identityReference });
67
+ * } else {
68
+ * console.log('Not verified:', data.decision, data.resultText);
69
+ * }
70
+ */
71
+ verify(params = {}) {
72
+ const {
73
+ country, idNumber, idType = 'NATIONAL_ID', consent,
74
+ firstName, lastName, middleName, dob, phoneNumber,
75
+ forceRefresh, idempotencyKey,
76
+ } = params;
77
+
78
+ // Fail fast on the client so an obviously-malformed call never costs a
79
+ // round-trip (or a charge).
80
+ if (!country) throw new Error('identity.verify: country is required (e.g. "KE")');
81
+ if (!idNumber) throw new Error('identity.verify: idNumber is required');
82
+ if (!consent || consent.obtained !== true) {
83
+ throw new Error(
84
+ 'identity.verify: consent.obtained must be true — you must attest that the data ' +
85
+ 'subject consented to this verification. Pass consent: { obtained: true, reference?, purpose? }'
86
+ );
87
+ }
88
+
89
+ const ctry = String(country).toUpperCase().trim();
90
+ const type = String(idType).toUpperCase().trim();
91
+ if (SUPPORTED[ctry] && !SUPPORTED[ctry].includes(type)) {
92
+ throw new Error(`identity.verify: idType '${type}' is not supported for ${ctry} (supported: ${SUPPORTED[ctry].join(', ')})`);
93
+ }
94
+
95
+ const body = {
96
+ country: ctry,
97
+ idType: type,
98
+ idNumber: String(idNumber).trim(),
99
+ consent: {
100
+ obtained: true,
101
+ ...(consent.reference ? { reference: consent.reference } : {}),
102
+ ...(consent.purpose ? { purpose: consent.purpose } : {}),
103
+ },
104
+ };
105
+ if (firstName) body.firstName = firstName;
106
+ if (lastName) body.lastName = lastName;
107
+ if (middleName) body.middleName = middleName;
108
+ if (dob) body.dob = dob;
109
+ if (phoneNumber) body.phoneNumber = phoneNumber;
110
+ if (forceRefresh === true) body.forceRefresh = true;
111
+
112
+ return this._http.post('/sdk/v1/identity/verify', {
113
+ data: body,
114
+ ...(idempotencyKey ? { idempotencyKey } : {}),
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Convenience wrapper: resolve to `true` / `false` instead of the full record.
120
+ * Any API error still throws — a thrown error means "we could not check",
121
+ * which is NOT the same as "not verified", so don't collapse the two.
122
+ *
123
+ * @param {Object} params - Same shape as {@link IdentityResource#verify}
124
+ * @returns {Promise<boolean>}
125
+ *
126
+ * @example
127
+ * if (await client.identity.isVerified({
128
+ * country: 'KE', idNumber: '12345678',
129
+ * consent: { obtained: true, purpose: 'KYC onboarding' }
130
+ * })) { … }
131
+ */
132
+ async isVerified(params = {}) {
133
+ const res = await this.verify(params);
134
+ return res?.data?.verified === true;
135
+ }
136
+
137
+ /** Countries and ID types this SDK build knows the service supports. */
138
+ static get supported() {
139
+ return SUPPORTED;
140
+ }
141
+ }
142
+
143
+ module.exports = { IdentityResource };