@dossier-wiki/cloud-sdk 0.1.0 → 0.2.0-staging.568b313

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.
Files changed (120) hide show
  1. package/README.md +697 -46
  2. package/dist/cjs/api.js +34 -0
  3. package/dist/cjs/api.js.map +1 -0
  4. package/dist/cjs/error.js +29 -0
  5. package/dist/cjs/error.js.map +1 -0
  6. package/dist/cjs/http.js +75 -0
  7. package/dist/cjs/http.js.map +1 -0
  8. package/dist/cjs/index.js +48 -0
  9. package/dist/cjs/index.js.map +1 -0
  10. package/dist/cjs/resources/aml.js +88 -0
  11. package/dist/cjs/resources/aml.js.map +1 -0
  12. package/dist/cjs/resources/documents.js +170 -0
  13. package/dist/cjs/resources/documents.js.map +1 -0
  14. package/dist/cjs/resources/form-templates.js +117 -0
  15. package/dist/cjs/resources/form-templates.js.map +1 -0
  16. package/dist/cjs/resources/kyc/index.js +32 -0
  17. package/dist/cjs/resources/kyc/index.js.map +1 -0
  18. package/dist/cjs/resources/kyc/sessions.js +103 -0
  19. package/dist/cjs/resources/kyc/sessions.js.map +1 -0
  20. package/dist/cjs/resources/kyc/workflows.js +150 -0
  21. package/dist/cjs/resources/kyc/workflows.js.map +1 -0
  22. package/dist/cjs/resources/media.js +62 -0
  23. package/dist/cjs/resources/media.js.map +1 -0
  24. package/dist/cjs/schemas/index.js +9 -0
  25. package/dist/cjs/schemas/index.js.map +1 -0
  26. package/dist/cjs/schemas/kyc/workflow-steps.js +145 -0
  27. package/dist/cjs/schemas/kyc/workflow-steps.js.map +1 -0
  28. package/dist/cjs/service.js +132 -0
  29. package/dist/cjs/service.js.map +1 -0
  30. package/dist/cjs/session.js +379 -0
  31. package/dist/cjs/session.js.map +1 -0
  32. package/dist/cjs/types.js +36 -0
  33. package/dist/cjs/types.js.map +1 -0
  34. package/dist/cjs/webhooks.js +93 -0
  35. package/dist/cjs/webhooks.js.map +1 -0
  36. package/dist/esm/api.js +33 -0
  37. package/dist/esm/api.js.map +1 -0
  38. package/dist/esm/error.js +25 -0
  39. package/dist/esm/error.js.map +1 -0
  40. package/dist/esm/http.js +71 -0
  41. package/dist/esm/http.js.map +1 -0
  42. package/dist/esm/index.js +19 -0
  43. package/dist/esm/index.js.map +1 -0
  44. package/dist/esm/resources/aml.js +84 -0
  45. package/dist/esm/resources/aml.js.map +1 -0
  46. package/dist/esm/resources/documents.js +166 -0
  47. package/dist/esm/resources/documents.js.map +1 -0
  48. package/dist/esm/resources/form-templates.js +113 -0
  49. package/dist/esm/resources/form-templates.js.map +1 -0
  50. package/dist/esm/resources/kyc/index.js +26 -0
  51. package/dist/esm/resources/kyc/index.js.map +1 -0
  52. package/dist/esm/resources/kyc/sessions.js +99 -0
  53. package/dist/esm/resources/kyc/sessions.js.map +1 -0
  54. package/dist/esm/resources/kyc/workflows.js +146 -0
  55. package/dist/esm/resources/kyc/workflows.js.map +1 -0
  56. package/dist/esm/resources/media.js +58 -0
  57. package/dist/esm/resources/media.js.map +1 -0
  58. package/dist/esm/schemas/index.js +2 -0
  59. package/dist/esm/schemas/index.js.map +1 -0
  60. package/dist/esm/schemas/kyc/workflow-steps.js +142 -0
  61. package/dist/esm/schemas/kyc/workflow-steps.js.map +1 -0
  62. package/dist/esm/service.js +127 -0
  63. package/dist/esm/service.js.map +1 -0
  64. package/dist/esm/session.js +375 -0
  65. package/dist/esm/session.js.map +1 -0
  66. package/dist/{types.js → esm/types.js} +11 -27
  67. package/dist/esm/types.js.map +1 -0
  68. package/dist/{webhooks.js → esm/webhooks.js} +10 -11
  69. package/dist/esm/webhooks.js.map +1 -0
  70. package/dist/{api.d.ts → types/api.d.ts} +297 -166
  71. package/dist/types/api.d.ts.map +1 -0
  72. package/dist/types/error.d.ts +24 -0
  73. package/dist/types/error.d.ts.map +1 -0
  74. package/dist/types/http.d.ts +17 -0
  75. package/dist/types/http.d.ts.map +1 -0
  76. package/dist/types/index.d.ts +15 -0
  77. package/dist/types/index.d.ts.map +1 -0
  78. package/dist/types/resources/aml.d.ts +79 -0
  79. package/dist/types/resources/aml.d.ts.map +1 -0
  80. package/dist/types/resources/documents.d.ts +153 -0
  81. package/dist/types/resources/documents.d.ts.map +1 -0
  82. package/dist/types/resources/form-templates.d.ts +104 -0
  83. package/dist/types/resources/form-templates.d.ts.map +1 -0
  84. package/dist/types/resources/kyc/index.d.ts +34 -0
  85. package/dist/types/resources/kyc/index.d.ts.map +1 -0
  86. package/dist/types/resources/kyc/sessions.d.ts +92 -0
  87. package/dist/types/resources/kyc/sessions.d.ts.map +1 -0
  88. package/dist/types/resources/kyc/workflows.d.ts +135 -0
  89. package/dist/types/resources/kyc/workflows.d.ts.map +1 -0
  90. package/dist/types/resources/media.d.ts +56 -0
  91. package/dist/types/resources/media.d.ts.map +1 -0
  92. package/dist/types/schemas/index.d.ts +3 -0
  93. package/dist/types/schemas/index.d.ts.map +1 -0
  94. package/dist/types/schemas/kyc/workflow-steps.d.ts +5113 -0
  95. package/dist/types/schemas/kyc/workflow-steps.d.ts.map +1 -0
  96. package/dist/types/service.d.ts +143 -0
  97. package/dist/types/service.d.ts.map +1 -0
  98. package/dist/types/session.d.ts +176 -0
  99. package/dist/types/session.d.ts.map +1 -0
  100. package/dist/{types.d.ts → types/types.d.ts} +100 -2
  101. package/dist/types/types.d.ts.map +1 -0
  102. package/dist/{webhooks.d.ts → types/webhooks.d.ts} +5 -5
  103. package/dist/types/webhooks.d.ts.map +1 -0
  104. package/package.json +26 -32
  105. package/dist/api.d.ts.map +0 -1
  106. package/dist/api.js +0 -22
  107. package/dist/api.js.map +0 -1
  108. package/dist/index.d.ts +0 -6
  109. package/dist/index.d.ts.map +0 -1
  110. package/dist/index.js +0 -5
  111. package/dist/index.js.map +0 -1
  112. package/dist/service.d.ts +0 -39
  113. package/dist/service.d.ts.map +0 -1
  114. package/dist/service.js +0 -150
  115. package/dist/service.js.map +0 -1
  116. package/dist/types.d.ts.map +0 -1
  117. package/dist/types.js.map +0 -1
  118. package/dist/webhooks.d.ts.map +0 -1
  119. package/dist/webhooks.js.map +0 -1
  120. package/docs/REFERENCE.md +0 -114
package/README.md CHANGED
@@ -1,88 +1,739 @@
1
1
  # @dossier-wiki/cloud-sdk
2
2
 
3
- TypeScript SDK for the [Dossiers Cloud API](https://api.dossiers.wiki) — sanctions search, high-risk dossier search, screening requests, KYC sessions, document processing, and webhook handling.
3
+ The official TypeScript SDK for the Dossiers API.
4
4
 
5
- ## Install
5
+ Covers AML screening, KYC session management, AI-powered document processing,
6
+ form templates, and webhook verification — all with full TypeScript types and
7
+ optional Zod runtime validation.
6
8
 
7
- ```sh
9
+ ## Table of Contents
10
+
11
+ - [Installation](#installation)
12
+ - [Quick Start](#quick-start)
13
+ - [Migrating from the flat API (v0.1)](#migrating-from-the-flat-api-v01)
14
+ - [Authentication](#authentication)
15
+ - [Error Handling](#error-handling)
16
+ - [AML Screening](#aml-screening)
17
+ - [Sanctions Search](#sanctions-search)
18
+ - [PEP & Adverse Media (Dossiers)](#pep--adverse-media-dossiers)
19
+ - [Ongoing Screening Requests](#ongoing-screening-requests)
20
+ - [KYC Sessions](#kyc-sessions)
21
+ - [KYC Workflows](#kyc-workflows)
22
+ - [Building a Workflow](#building-a-workflow)
23
+ - [Zod Validation](#zod-validation)
24
+ - [Document Processing](#document-processing)
25
+ - [Initiate a Job](#initiate-a-job)
26
+ - [Poll for Results](#poll-for-results)
27
+ - [Real-Time Progress (SSE)](#real-time-progress-sse)
28
+ - [Typed Extraction Schemas](#typed-extraction-schemas)
29
+ - [Form Templates](#form-templates)
30
+ - [Media](#media)
31
+ - [Webhooks](#webhooks)
32
+ - [TypeScript](#typescript)
33
+
34
+ ---
35
+
36
+ ## Installation
37
+
38
+ ```bash
8
39
  npm install @dossier-wiki/cloud-sdk
9
40
  # or
10
- bun add @dossier-wiki/cloud-sdk
41
+ pnpm add @dossier-wiki/cloud-sdk
11
42
  ```
12
43
 
13
- ## Quick start
44
+ Requires Node.js 18+ (or any runtime with a global `fetch` and `crypto.subtle`).
45
+
46
+ ---
47
+
48
+ ## Quick Start
14
49
 
15
50
  ```ts
16
51
  import { DossiersAPIService } from '@dossier-wiki/cloud-sdk';
17
52
 
18
- const dossiers = new DossiersAPIService({
19
- apiKey: process.env.DOSSIERS_API_KEY!,
53
+ const service = new DossiersAPIService({
54
+ apiKey: process.env.DOSSIERS_API_KEY!,
20
55
  });
21
56
 
22
- const hits = await dossiers.searchSanctions({
23
- name: 'Acme Holdings',
24
- threshold: 0.8,
57
+ // AML search sanctions
58
+ const { totalHits, documents } = await service.aml.searchSanctions({
59
+ fullName: 'John Doe',
60
+ });
61
+
62
+ // KYC — create a session
63
+ const { sessionUrl } = await service.kyc.sessions.create({
64
+ customer: { name: 'Alice Brown', email: 'alice@example.com' },
65
+ workflowId: 'wf_abc123',
66
+ });
67
+
68
+ // Document processing
69
+ const instance = await service.documents.initiate({
70
+ processorId: 'INVOICE',
71
+ files: [{ url: 'https://example.com/invoice.pdf', mimeType: 'application/pdf' }],
25
72
  });
26
73
  ```
27
74
 
28
- ### Webhooks
75
+ ---
76
+
77
+ ## Migrating from the flat API (v0.1)
78
+
79
+ Version 0.1 exposed every operation as a method directly on the service
80
+ (`service.searchSanctions()`, `service.createKycSession()`, ...). That top-level
81
+ surface grew too large to navigate, so operations are now grouped into resource
82
+ namespaces.
83
+
84
+ **The old flat methods still work** — they are thin, deprecated wrappers that
85
+ forward to the new resource methods, so existing code keeps running unchanged.
86
+ Your editor will show them struck through; migrate at your own pace. They will
87
+ be removed in a future major version.
88
+
89
+ | Deprecated flat method | Use instead |
90
+ |------------------------|-------------|
91
+ | `service.searchSanctions(input)` | `service.aml.searchSanctions(input)` |
92
+ | `service.searchDossiers(input)` | `service.aml.searchDossiers(input)` |
93
+ | `service.createScreeningRequest(input)` | `service.aml.createScreeningRequest(input)` |
94
+ | `service.createKycSession(input)` | `service.kyc.sessions.create(input)` |
95
+ | `service.listKycSessions(filters?)` | `service.kyc.sessions.list(filters?)` |
96
+ | `service.getKycSession(id)` | `service.kyc.sessions.get(id)` |
97
+ | `service.deleteKycSession(id)` | `service.kyc.sessions.delete(id)` |
98
+ | `service.initiateDocumentProcessing(input)` | `service.documents.initiate(input)` |
99
+ | `service.listDocumentProcessingInstances(query?)` | `service.documents.list(query?)` |
100
+ | `service.listDocumentProcessors()` | `service.documents.listProcessors()` |
101
+ | `service.getDocumentProcessingInstance(id)` | `service.documents.get(id)` |
102
+ | `service.getDocumentProcessingStage(id, stageId)` | `service.documents.getStage(id, stageId)` |
103
+ | `service.deleteDocumentProcessingInstance(id)` | `service.documents.delete(id)` |
104
+ | `service.streamDocumentProcessingEvents(id, init?)` | `service.documents.stream(id, init?)` |
105
+
106
+ New capabilities added since v0.1 — KYC workflows (`service.kyc.workflows`),
107
+ form templates (`service.formTemplates`), media access (`service.media`), and
108
+ KYC session webhook events — are only available on the resource API.
109
+
110
+ ---
111
+
112
+ ## Authentication
113
+
114
+ All requests are authenticated with your API key as a Bearer token. Set it
115
+ once on the service instance — never include it in client-side code.
29
116
 
30
117
  ```ts
31
- import {
32
- DossiersWebhookRouter,
33
- DossiersWebhookEventTypes,
34
- } from '@dossier-wiki/cloud-sdk';
118
+ const service = new DossiersAPIService({
119
+ apiKey: process.env.DOSSIERS_API_KEY!,
35
120
 
36
- const router = new DossiersWebhookRouter(process.env.DOSSIERS_WEBHOOK_SECRET!);
121
+ // Optional: override the base URL (e.g. for local development)
122
+ baseUrl: 'http://localhost:4000',
123
+
124
+ // Optional: inject a custom fetch (useful for testing or proxying)
125
+ fetch: myCustomFetch,
126
+ });
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Error Handling
37
132
 
38
- router.on(
39
- DossiersWebhookEventTypes.DOCUMENT_VERIFICATION_INSTANCE_UPDATED,
40
- async ({ data }) => {
41
- if (data.status === 'completed') {
42
- // ... handle completion
43
- }
133
+ All SDK methods throw a `DossiersAPIError` on non-2xx responses.
134
+
135
+ ```ts
136
+ import { DossiersAPIService, DossiersAPIError } from '@dossier-wiki/cloud-sdk';
137
+
138
+ try {
139
+ const session = await service.kyc.sessions.get('ses_nonexistent');
140
+ } catch (err) {
141
+ if (err instanceof DossiersAPIError) {
142
+ console.error(err.status); // e.g. 404
143
+ console.error(err.message); // human-readable message from the API
144
+ console.error(err.body); // raw parsed response body
145
+ }
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ## AML Screening
152
+
153
+ ### Sanctions Search
154
+
155
+ Search global sanctions lists (OFAC, UN, FCDO, DFAT, CASL, INTERPOL) for
156
+ individuals or entities.
157
+
158
+ ```ts
159
+ const results = await service.aml.searchSanctions({
160
+ fullName: 'Jane Smith',
161
+
162
+ // Optional: narrow to specific lists
163
+ sources: ['ofac', 'un'],
164
+
165
+ // Optional: filter by country
166
+ countries: ['US', 'GB'],
167
+
168
+ // Optional: match against document numbers
169
+ idNumbers: ['P123456789'],
170
+ });
171
+
172
+ console.log(results.totalHits);
173
+ for (const sanction of results.documents) {
174
+ console.log(sanction.name, sanction.source, sanction.sanctionsProgramme);
175
+ }
176
+ ```
177
+
178
+ ### PEP & Adverse Media (Dossiers)
179
+
180
+ Search PEP registries and adverse media databases. Returns individual dossiers
181
+ with risk scores, positions held, and associates.
182
+
183
+ ```ts
184
+ const dossiers = await service.aml.searchDossiers({
185
+ fullName: 'Robert Jones',
186
+ onlyShowHighRiskProfiles: true,
187
+ noCache: false, // use cached results (default)
188
+ });
189
+
190
+ for (const dossier of dossiers) {
191
+ const score = dossier.riskScorecard?.overallRiskScore ?? 0;
192
+ console.log(dossier.fullName, score, dossier.riskScorecard?.riskCategory);
193
+ }
194
+ ```
195
+
196
+ ### Ongoing Screening Requests
197
+
198
+ Submit individuals for continuous monitoring. Webhook events fire when
199
+ screening completes or when new alerts are triggered.
200
+
201
+ ```ts
202
+ const { requestId } = await service.aml.createScreeningRequest({
203
+ fullName: 'Alice Brown',
204
+ externalId: 'cust_123', // your internal reference ID
205
+ dateOfBirth: '1985-06-15',
206
+ country: 'GB',
207
+ identificationDocument: {
208
+ type: 'passport',
209
+ number: 'AB123456',
210
+ country: 'GB',
211
+ },
212
+ });
213
+ ```
214
+
215
+ ---
216
+
217
+ ## KYC Sessions
218
+
219
+ A KYC session is a single customer verification journey. The customer receives
220
+ a link (`sessionUrl`) they visit to complete the configured workflow steps.
221
+
222
+ ```ts
223
+ // Create a session
224
+ const { session, sessionUrl, token } = await service.kyc.sessions.create({
225
+ customer: {
226
+ name: 'Nimal Perera',
227
+ email: 'nimal@example.com',
228
+ externalId: 'cust_001', // your internal reference
229
+ },
230
+ workflowId: 'wf_standard_kyc',
231
+ expiresInMs: 86_400_000, // 24 hours (default: 1 hour)
232
+ });
233
+
234
+ // Send the sessionUrl to your customer
235
+ await sendInvitationEmail(session.customer.email, sessionUrl);
236
+
237
+ // List sessions
238
+ const { sessions, totalCount } = await service.kyc.sessions.list({
239
+ isComplete: true,
240
+ sortBy: 'completedAt',
241
+ sortOrder: 'desc',
242
+ limit: 50,
243
+ offset: 0,
244
+ });
245
+
246
+ // Get a specific session (includes full step history & media)
247
+ const fullSession = await service.kyc.sessions.get(session.id);
248
+ console.log(fullSession.status); // 'complete' | 'in-progress' | 'approved' | ...
249
+
250
+ // Delete a session
251
+ await service.kyc.sessions.delete(session.id);
252
+ ```
253
+
254
+ ---
255
+
256
+ ## KYC Workflows
257
+
258
+ A workflow defines the ordered steps a customer goes through during a session
259
+ (e.g. identity verification → liveness → form collection).
260
+
261
+ ### Building a Workflow
262
+
263
+ Use `stepDefaults()` to get pre-built configurations for each step type, then
264
+ assemble them into a workflow.
265
+
266
+ ```ts
267
+ import type { WorkflowRules } from '@dossier-wiki/cloud-sdk';
268
+
269
+ // Fetch defaults for all step types
270
+ const defaults = await service.kyc.workflows.stepDefaults();
271
+ const idStep = defaults.find(d => d.type === 'id_verification')!;
272
+ const livenessStep = defaults.find(d => d.type === 'liveness')!;
273
+
274
+ // Create the workflow
275
+ const workflow = await service.kyc.workflows.create({
276
+ name: 'Standard KYC',
277
+ description: 'Identity check + liveness verification',
278
+ steps: [
279
+ {
280
+ stepId: idStep.defaultStepId,
281
+ type: 'id_verification',
282
+ input: idStep.defaultInput as { config?: Record<string, unknown> },
283
+ output: { schema: idStep.schema },
284
+ rules: idStep.defaultRules as WorkflowRules,
285
+ locales: idStep.defaultLocales,
286
+ skippable: idStep.skippable,
287
+ },
288
+ {
289
+ stepId: livenessStep.defaultStepId,
290
+ type: 'liveness',
291
+ input: livenessStep.defaultInput as { config?: Record<string, unknown> },
292
+ output: { schema: livenessStep.schema },
293
+ rules: livenessStep.defaultRules as WorkflowRules,
294
+ locales: livenessStep.defaultLocales,
295
+ skippable: livenessStep.skippable,
44
296
  },
45
- );
297
+ ],
298
+ });
299
+
300
+ // Use the workflow when creating sessions
301
+ const { sessionUrl } = await service.kyc.sessions.create({
302
+ customer: { name: 'Bob Lee' },
303
+ workflowId: workflow.id,
304
+ });
305
+ ```
306
+
307
+ ### Zod Validation
308
+
309
+ The SDK exports Zod schemas for the step discriminated union, so you can
310
+ validate workflow input at runtime before sending it to the API.
311
+
312
+ ```ts
313
+ import { schemas } from '@dossier-wiki/cloud-sdk';
314
+
315
+ // Validates the full workflow creation input including per-step type constraints
316
+ const validated = schemas.CreateWorkflowSchema.parse(rawInput);
317
+ const workflow = await service.kyc.workflows.create(validated);
318
+
319
+ // Validate a single step
320
+ const step = schemas.WorkflowStepSchema.parse({
321
+ stepId: 'step_liveness',
322
+ type: 'liveness',
323
+ input: { config: { challengeType: 'selfie', timeLimitSeconds: 30 } },
324
+ output: {},
325
+ rules: { onSuccess: 'COMPLETE_SESSION', onFailure: 'FAIL_SESSION' },
326
+ });
327
+ ```
328
+
329
+ Available step types and their config constraints:
330
+
331
+ | Type | Config fields |
332
+ |------|--------------|
333
+ | `id_verification` | `strategy`, `allowedDocuments`, `maxSubmissions`, `allowUpload` |
334
+ | `document_collection` | `allowedDocuments`, `allowUpload`, `maxSubmissions` |
335
+ | `liveness` | `challengeType`, `timeLimitSeconds`, `minFaceLuminance`, `minFaceDetectionConfidence` |
336
+ | `form_collection` | `templateId`, `fields` |
337
+ | `esignature` | `signatureTypesAllowed` |
338
+ | `action` | `actionType` |
339
+
340
+ Other workflow operations:
341
+
342
+ ```ts
343
+ // List all workflows
344
+ const workflows = await service.kyc.workflows.list();
345
+
346
+ // Get a specific workflow
347
+ const wf = await service.kyc.workflows.get('wf_abc123');
46
348
 
47
- // In any Fetch-API server (Hono, Bun.serve, Cloudflare Workers, etc.):
48
- export default {
49
- fetch: (req: Request) => router.handle(req),
349
+ // Update (creates a new version; existing sessions are unaffected)
350
+ const updated = await service.kyc.workflows.update('wf_abc123', {
351
+ name: 'Standard KYC v2',
352
+ });
353
+
354
+ // Delete
355
+ await service.kyc.workflows.delete('wf_old123');
356
+ ```
357
+
358
+ ---
359
+
360
+ ## Document Processing
361
+
362
+ Initiate AI extraction jobs on uploaded documents. The pipeline validates the
363
+ document, extracts structured data, and runs post-processing checks.
364
+
365
+ Available processor IDs:
366
+
367
+ | `processorId` | Document |
368
+ |---------------|---------|
369
+ | `INVOICE` | Invoice / bill |
370
+ | `SRI_LANKA_ID` | Sri Lanka National Identity Card |
371
+ | `SRI_LANKA_FORM1_COMPANY_REGISTRATION` | Company Registration Form 1 |
372
+ | `SRI_LANKA_FORM15_ANNUAL_RETURN` | Annual Return Form 15 |
373
+
374
+ ### Initiate a Job
375
+
376
+ ```ts
377
+ const instance = await service.documents.initiate({
378
+ processorId: 'INVOICE',
379
+ files: [
380
+ { url: 'https://example.com/invoice.pdf', mimeType: 'application/pdf' },
381
+ ],
382
+
383
+ // Optional: cross-check context for the AI
384
+ inputData: {
385
+ expectedVendor: 'ACME Corp',
386
+ expectedAmount: '1500.00',
387
+ },
388
+
389
+ // Optional: arbitrary metadata stored on the instance
390
+ metadata: { orderId: 'ord_456' },
391
+
392
+ // Default: true — runs asynchronously; false waits for completion
393
+ backgroundProcessing: true,
394
+ });
395
+
396
+ console.log(instance.instanceId); // use this to poll or stream
397
+ ```
398
+
399
+ ### Poll for Results
400
+
401
+ ```ts
402
+ let instance = await service.documents.get(instanceId);
403
+
404
+ while (instance.status === 'pending' || instance.status === 'running') {
405
+ await new Promise(r => setTimeout(r, 2000));
406
+ instance = await service.documents.get(instanceId);
407
+ }
408
+
409
+ if (instance.status === 'completed') {
410
+ console.log(instance.result?.validation.passed);
411
+ console.log(instance.result?.normalizedData);
412
+ }
413
+ ```
414
+
415
+ ### Real-Time Progress (SSE)
416
+
417
+ ```ts
418
+ const res = await service.documents.stream(instanceId);
419
+ const reader = res.body!.getReader();
420
+ const decoder = new TextDecoder();
421
+
422
+ while (true) {
423
+ const { done, value } = await reader.read();
424
+ if (done) break;
425
+ const text = decoder.decode(value, { stream: true });
426
+ // Each SSE line is a DocumentProcessingEvent JSON payload
427
+ console.log(text);
428
+ }
429
+ ```
430
+
431
+ ### Typed Extraction Schemas
432
+
433
+ Use the generic parameter to type the `result.schema` field for a specific processor:
434
+
435
+ ```ts
436
+ import type {
437
+ DocumentProcessingExtractedField,
438
+ DocumentProcessingTransliteratedField,
439
+ } from '@dossier-wiki/cloud-sdk';
440
+
441
+ type SriLankaIdSchema = {
442
+ name: DocumentProcessingTransliteratedField;
443
+ idNumber: DocumentProcessingExtractedField<string>;
444
+ dateOfBirth: DocumentProcessingExtractedField<string>;
445
+ address: DocumentProcessingTransliteratedField;
446
+ gender: DocumentProcessingExtractedField<string>;
50
447
  };
448
+
449
+ const instance = await service.documents.initiate<SriLankaIdSchema>({
450
+ processorId: 'SRI_LANKA_ID',
451
+ files: [{ base64Data: '...', mimeType: 'image/jpeg' }],
452
+ });
453
+
454
+ // result.schema is now typed as SriLankaIdSchema
455
+ const name = instance.result?.schema.name.enTransliterations[0].transliteratedText;
456
+ ```
457
+
458
+ Other document processing operations:
459
+
460
+ ```ts
461
+ // List instances
462
+ const { instances, count } = await service.documents.list({
463
+ processorId: 'INVOICE',
464
+ status: 'completed',
465
+ limit: 20,
466
+ });
467
+
468
+ // Get a specific stage result
469
+ const stage = await service.documents.getStage(instanceId, 'post_validation');
470
+ console.log(stage.data.validation.passed);
471
+
472
+ // List available processors
473
+ const processors = await service.documents.listProcessors();
474
+
475
+ // Delete an instance
476
+ await service.documents.delete(instanceId);
51
477
  ```
52
478
 
53
- The router verifies the `x-webhook-signature` header (HMAC-SHA256 of the raw body) using the secret you pass to the constructor. Invalid signatures get a `401`; unhandled event types return `200 ok` so Dossiers does not retry.
479
+ ---
480
+
481
+ ## Form Templates
54
482
 
55
- ## Configuration
483
+ Form templates define reusable sets of fields for the `form_collection` KYC
484
+ workflow step.
56
485
 
57
486
  ```ts
58
- new DossiersAPIService({
59
- apiKey: '...', // required issued in the Dossiers dashboard
60
- baseUrl: '...', // optional — defaults to https://api.dossiers.wiki
61
- fetch: customFetch, // optional — inject a fetch implementation (e.g. for testing)
487
+ // Create a template
488
+ const template = await service.formTemplates.create({
489
+ name: 'Employment Details',
490
+ fields: [
491
+ {
492
+ id: 'employer',
493
+ type: 'text',
494
+ label: 'Employer Name',
495
+ required: true,
496
+ },
497
+ {
498
+ id: 'salary',
499
+ type: 'currency',
500
+ label: 'Annual Salary (LKR)',
501
+ required: true,
502
+ validation: { min: 0 },
503
+ },
504
+ {
505
+ id: 'employment_type',
506
+ type: 'select',
507
+ label: 'Employment Type',
508
+ required: true,
509
+ options: [
510
+ { value: 'full_time', label: 'Full Time' },
511
+ { value: 'part_time', label: 'Part Time' },
512
+ { value: 'self_employed', label: 'Self Employed' },
513
+ ],
514
+ },
515
+ ],
62
516
  });
517
+
518
+ // Reference the template in a form_collection step config
519
+ const workflowStep = {
520
+ type: 'form_collection' as const,
521
+ input: { config: { templateId: template.id } },
522
+ // ...
523
+ };
524
+
525
+ // List, get, update, delete
526
+ const { templates } = await service.formTemplates.list({ searchQuery: 'employment' });
527
+ const full = await service.formTemplates.get(template.id);
528
+ await service.formTemplates.update(template.id, { name: 'Employment Details v2' });
529
+ await service.formTemplates.delete(template.id);
63
530
  ```
64
531
 
65
- ## Error handling
532
+ Available field types: `text`, `long_text`, `number`, `currency`, `country`,
533
+ `language`, `email`, `phone`, `date`, `select`, `multi_select`, `dropdown`,
534
+ `toggle`, `boolean`, `file`, `heading`, `paragraph`, `description`, `divider`.
535
+
536
+ ---
537
+
538
+ ## Media
66
539
 
67
- All non-2xx responses throw `DossiersAPIError`:
540
+ Fetch binary files (images, videos, PDF signatures) from session results and
541
+ document processing instances using signed storage tokens.
68
542
 
69
543
  ```ts
70
- import { DossiersAPIError } from '@dossier-wiki/cloud-sdk';
544
+ // Tokens appear in session result media arrays and processing instance files
545
+ const token = session.result.steps[0].summary?.media[0]?.key;
71
546
 
72
- try {
73
- await dossiers.getKycSession('missing-id');
74
- } catch (err) {
75
- if (err instanceof DossiersAPIError) {
76
- console.log(err.status); // HTTP status
77
- console.log(err.body); // parsed JSON or raw text
78
- }
547
+ if (token) {
548
+ const res = await service.media.get(token);
549
+
550
+ // Browser convert to object URL for display
551
+ const blob = await res.blob();
552
+ const objectUrl = URL.createObjectURL(blob);
553
+ imageElement.src = objectUrl;
554
+
555
+ // Node.js — pipe to disk
556
+ const { createWriteStream } = await import('fs');
557
+ const { Readable } = await import('stream');
558
+ Readable.fromWeb(res.body!).pipe(createWriteStream('./capture.jpg'));
79
559
  }
80
560
  ```
81
561
 
82
- ## API reference
562
+ ---
563
+
564
+ ## Webhooks
83
565
 
84
- See [docs/REFERENCE.md](./docs/REFERENCE.md) for the full method list.
566
+ Use `DossiersWebhookRouter` to handle incoming webhook events with HMAC-SHA256
567
+ signature verification.
85
568
 
86
- ## License
569
+ ```ts
570
+ import { DossiersWebhookRouter, DossiersWebhookEventTypes } from '@dossier-wiki/cloud-sdk';
87
571
 
88
- MIT
572
+ const router = new DossiersWebhookRouter(process.env.DOSSIERS_WEBHOOK_SECRET!);
573
+
574
+ router
575
+ .on(DossiersWebhookEventTypes.DOCUMENT_VERIFICATION_INSTANCE_COMPLETED, async (event, ctx) => {
576
+ const { documentProcessorInstanceId } = event.data;
577
+ const instance = await service.documents.get(documentProcessorInstanceId);
578
+ await saveResult(instance);
579
+ })
580
+ .on(DossiersWebhookEventTypes.KYC_SESSION_COMPLETED, async (event, ctx) => {
581
+ // Payloads carry no PII — fetch the full session on demand.
582
+ const { kycSessionId, outcome } = event.data;
583
+ const session = await service.kyc.sessions.get(kycSessionId);
584
+ await recordVerification(session, outcome); // outcome: 'complete' | 'failed'
585
+ })
586
+ .on(DossiersWebhookEventTypes.AML_SCREENING_ALERT_TRIGGERED, async (event, ctx) => {
587
+ console.log('Alert triggered for screening request', event.data);
588
+ });
589
+
590
+ // Express
591
+ app.post('/webhooks/dossiers', async (req, res) => {
592
+ // Reconstruct a Web API Request from the Express request
593
+ const body = await getRawBody(req);
594
+ const webReq = new Request('https://your-server/webhooks/dossiers', {
595
+ method: 'POST',
596
+ headers: req.headers as HeadersInit,
597
+ body,
598
+ });
599
+ const response = await router.handle(webReq);
600
+ res.status(response.status).send(await response.text());
601
+ });
602
+
603
+ // Next.js App Router
604
+ export async function POST(req: Request) {
605
+ return router.handle(req, { waitUntil: (p) => context.waitUntil(p) });
606
+ }
607
+ ```
608
+
609
+ Webhook events are acknowledged immediately (200 OK) before the handler runs —
610
+ a slow handler will not cause the platform to retry. If your handler throws,
611
+ the error is logged but no retry occurs. Implement your own retry logic inside
612
+ the handler if needed.
613
+
614
+ Every event delivered to a handler has the shape:
615
+
616
+ ```ts
617
+ {
618
+ id: string; // e.g. "evt_1712345678_ab12cd34"
619
+ type: DossiersWebhookEventTypes;
620
+ organizationId: string;
621
+ eventTriggeredAt: string; // ISO 8601
622
+ data: /* typed per event — see below */;
623
+ }
624
+ ```
625
+
626
+ The `data` field is fully typed by the event `type` you pass to `.on()`. Import
627
+ `DossiersWebhookPayload` for the discriminated union of all event shapes.
628
+
629
+ ### Available Event Types
630
+
631
+ **Document processing**
632
+
633
+ | Enum value | Event string | `data` type |
634
+ |------------|-------------|-------------|
635
+ | `DOCUMENT_VERIFICATION_INSTANCE_CREATED` | `documentVerification.instance.created` | `DocumentVerificationInstanceCreatedData` |
636
+ | `DOCUMENT_VERIFICATION_INSTANCE_UPDATED` | `documentVerification.instance.updated` | `DocumentVerificationInstanceUpdatedData` |
637
+ | `DOCUMENT_VERIFICATION_INSTANCE_COMPLETED` | `documentVerification.instance.completed` | `DocumentVerificationInstanceCompletedData` |
638
+ | `DOCUMENT_VERIFICATION_INSTANCE_DELETED` | `documentVerification.instance.deleted` | `DocumentVerificationInstanceDeletedData` |
639
+
640
+ **KYC sessions**
641
+
642
+ | Enum value | Event string | `data` type |
643
+ |------------|-------------|-------------|
644
+ | `KYC_SESSION_CREATED` | `kycSessions.session.created` | `KycSessionEventData` |
645
+ | `KYC_SESSION_UPDATED` | `kycSessions.session.updated` | `KycSessionEventData` |
646
+ | `KYC_SESSION_COMPLETED` | `kycSessions.session.completed` | `KycSessionEventData` (with `outcome`) |
647
+ | `KYC_SESSION_REVIEWED` | `kycSessions.session.reviewed` | `KycSessionEventData` (with `review`) |
648
+ | `KYC_SESSION_DELETED` | `kycSessions.session.deleted` | `KycSessionDeletedData` |
649
+ | `KYC_STEP_COMPLETED` | `kycSessions.step.completed` | `KycStepEventData` |
650
+ | `KYC_STEP_FAILED` | `kycSessions.step.failed` | `KycStepEventData` |
651
+ | `KYC_SESSION_COMMENT_ADDED` | `kycSessions.session.commentAdded` | `KycSessionCommentAddedData` |
652
+ | `KYC_SESSION_COMMENT_UPDATED` | `kycSessions.session.commentUpdated` | `KycSessionCommentUpdatedData` |
653
+ | `KYC_SESSION_COMMENT_DELETED` | `kycSessions.session.commentDeleted` | `KycSessionCommentDeletedData` |
654
+
655
+ > KYC payloads carry **no PII** (no customer name/email, no step outputs). They
656
+ > reference a `kycSessionId` — fetch the detail you need with
657
+ > `service.kyc.sessions.get(kycSessionId)`.
658
+
659
+ **AML screening**
660
+
661
+ | Enum value | Event string |
662
+ |------------|-------------|
663
+ | `AML_SCREENING_REQUEST_CREATED` | `amlScreening.screeningRequest.created` |
664
+ | `AML_SCREENING_REQUEST_COMPLETED` | `amlScreening.screeningRequest.completed` |
665
+ | `AML_SCREENING_ALERT_TRIGGERED` | `amlScreening.screeningAlert.triggered` |
666
+ | `AML_SCREENING_ALERT_STATUS_CHANGED` | `amlScreening.screeningAlert.statusChanged` |
667
+ | `AML_SCREENING_ALERT_COMMENT_ADDED` | `amlScreening.screeningAlert.commentAdded` |
668
+ | `AML_SCREENING_ALERT_COMMENT_UPDATED` | `amlScreening.screeningAlert.commentUpdated` |
669
+ | `AML_SCREENING_ALERT_COMMENT_DELETED` | `amlScreening.screeningAlert.commentDeleted` |
670
+ | `AML_SCREENING_ALERT_DELETED` | `amlScreening.screeningAlert.deleted` |
671
+
672
+ **System**
673
+
674
+ | Enum value | Event string |
675
+ |------------|-------------|
676
+ | `SYSTEM_TEST_EVENT` | `system.testEvent` |
677
+ | `SYSTEM_PRODUCT_ACCESS_GRANTED` | `system.product.accessGranted` |
678
+ | `SYSTEM_PRODUCT_ACCESS_REVOKED` | `system.product.accessRevoked` |
679
+ | `SYSTEM_API_KEY_CREATED` | `system.apiKey.created` |
680
+ | `SYSTEM_API_KEY_INVALIDATED` | `system.apiKey.invalidated` |
681
+ | `SYSTEM_USAGE_LIMIT_REACHED` | `system.usage.limitReached` |
682
+ | `SYSTEM_USAGE_LIMIT_WARNING` | `system.usage.limitWarning` |
683
+
684
+ ---
685
+
686
+ ## TypeScript
687
+
688
+ All API request and response shapes are exported as TypeScript interfaces.
689
+ Import them as needed:
690
+
691
+ ```ts
692
+ import type {
693
+ // AML
694
+ SanctionSearchInput,
695
+ SanctionSearchResponse,
696
+ Sanction,
697
+ DossierSearchInput,
698
+ IndividualDossier,
699
+ CreateScreeningRequestDto,
700
+
701
+ // KYC Sessions
702
+ CreateKycSessionDTO,
703
+ KycSessionOutputDTO,
704
+ KycSessionFiltersDTO,
705
+ PaginatedKycSessionsDTO,
706
+
707
+ // KYC Workflows
708
+ CreateKycWorkflowInput,
709
+ KycWorkflow,
710
+ KycWorkflowStep,
711
+ KycWorkflowStepType,
712
+ WorkflowRules,
713
+ WorkflowStepDefault,
714
+
715
+ // Document Processing
716
+ InitiateDocumentProcessingInput,
717
+ DocumentProcessingInstance,
718
+ DocumentProcessingResult,
719
+ DocumentProcessingExtractedField,
720
+ DocumentProcessingTransliteratedField,
721
+ DocumentProcessorId,
722
+
723
+ // Form Templates
724
+ CreateFormTemplateInput,
725
+ FormTemplate,
726
+ FormField,
727
+ FormFieldType,
728
+
729
+ // Webhooks
730
+ DossiersWebhookPayload,
731
+ DossiersWebhookEvent,
732
+ KycSessionEventData,
733
+ KycSessionDeletedData,
734
+ KycStepEventData,
735
+ KycSessionCommentAddedData,
736
+ KycSessionCommentUpdatedData,
737
+ KycSessionCommentDeletedData,
738
+ } from '@dossier-wiki/cloud-sdk';
739
+ ```