dataspace-client-sdk-node 0.1.1

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 (56) hide show
  1. package/README.md +310 -0
  2. package/SDK_PARITY_MAP.md +120 -0
  3. package/TODO_PROMPT_NEXT_STEPS.md +185 -0
  4. package/artifacts/update-smart-wallet.js +1016 -0
  5. package/dist/builders.d.ts +12 -0
  6. package/dist/builders.js +17 -0
  7. package/dist/client.d.ts +333 -0
  8. package/dist/client.js +1229 -0
  9. package/dist/consent/pdfSignatureVerification.d.ts +18 -0
  10. package/dist/consent/pdfSignatureVerification.js +23 -0
  11. package/dist/index.d.ts +4 -0
  12. package/dist/index.js +8 -0
  13. package/dist/sdk/dataspace-wallet-sdk-node/MultiWalletClient.d.ts +9 -0
  14. package/dist/sdk/dataspace-wallet-sdk-node/MultiWalletClient.js +21 -0
  15. package/dist/sdk/dataspace-wallet-sdk-node/WalletClient.d.ts +26 -0
  16. package/dist/sdk/dataspace-wallet-sdk-node/WalletClient.js +36 -0
  17. package/dist/sdk/dataspace-wallet-sdk-node/index.d.ts +6 -0
  18. package/dist/sdk/dataspace-wallet-sdk-node/index.js +6 -0
  19. package/dist/sdk/dataspace-wallet-sdk-node/provider.d.ts +24 -0
  20. package/dist/sdk/dataspace-wallet-sdk-node/provider.js +1 -0
  21. package/dist/sdk/dataspace-wallet-sdk-node/providers/memory-provider.d.ts +41 -0
  22. package/dist/sdk/dataspace-wallet-sdk-node/providers/memory-provider.js +216 -0
  23. package/dist/sdk/dataspace-wallet-sdk-node/providers/seed-provider.d.ts +22 -0
  24. package/dist/sdk/dataspace-wallet-sdk-node/providers/seed-provider.js +28 -0
  25. package/dist/sdk/dataspace-wallet-sdk-node/types.d.ts +51 -0
  26. package/dist/sdk/dataspace-wallet-sdk-node/types.js +1 -0
  27. package/dist/types.d.ts +445 -0
  28. package/dist/types.js +1 -0
  29. package/docs/API.md +745 -0
  30. package/docs/DATA_MODEL_ALIGNMENT.md +31 -0
  31. package/docs/DATA_PLANES_SCOPE_MATRIX.md +51 -0
  32. package/docs/DEVELOPER_USE_CASES.md +253 -0
  33. package/docs/E2E_BOOTSTRAP.md +54 -0
  34. package/docs/TODO_SMART_EHR_COMPAT.md +58 -0
  35. package/examples/backend-pkce-auth.mjs +119 -0
  36. package/examples/conversion-upload.mjs +52 -0
  37. package/examples/e2e-bootstrap-tenant.mjs +126 -0
  38. package/examples/e2e-individual-flow.mjs +43 -0
  39. package/examples/host-activate-and-employee.mjs +75 -0
  40. package/package.json +26 -0
  41. package/src/builders.ts +28 -0
  42. package/src/client.ts +1626 -0
  43. package/src/consent/pdfSignatureVerification.ts +41 -0
  44. package/src/index.ts +8 -0
  45. package/src/sdk/dataspace-wallet-sdk-node/MultiWalletClient.ts +25 -0
  46. package/src/sdk/dataspace-wallet-sdk-node/WalletClient.ts +63 -0
  47. package/src/sdk/dataspace-wallet-sdk-node/index.ts +6 -0
  48. package/src/sdk/dataspace-wallet-sdk-node/provider.ts +44 -0
  49. package/src/sdk/dataspace-wallet-sdk-node/providers/memory-provider.ts +310 -0
  50. package/src/sdk/dataspace-wallet-sdk-node/providers/seed-provider.ts +31 -0
  51. package/src/sdk/dataspace-wallet-sdk-node/types.ts +61 -0
  52. package/src/types.ts +497 -0
  53. package/tests/client.test.mjs +892 -0
  54. package/tests/uc5-org-onboarding.flow.test.mjs +145 -0
  55. package/tests/uc5-subject-data.flow.test.mjs +198 -0
  56. package/tsconfig.json +13 -0
package/docs/API.md ADDED
@@ -0,0 +1,745 @@
1
+ # dataspace-client-sdk-node — API Reference
2
+
3
+ All methods belong to the `DataspaceNodeClient` class unless stated otherwise.
4
+
5
+ ## Contents
6
+
7
+ - [Setup](#setup)
8
+ - [Types](#types)
9
+ - [Constructor](#constructor)
10
+ - [Path helpers](#path-helpers)
11
+ - [Generic builders](#generic-builders)
12
+ - [Host registry (controller-level)](#host-registry-controller-level)
13
+ - [Individual — Family Organization](#individual--family-organization)
14
+ - [Individual — FHIR resources](#individual--fhir-resources)
15
+ - [Entity — Employee](#entity--employee)
16
+ - [Identity / Auth](#identity--auth)
17
+ - [DataConversion upload](#dataconversion-upload)
18
+ - [Task debug](#task-debug)
19
+ - [Auth methods](#auth-methods)
20
+ - [authenticateBackendPkceAndExchange](#authenticatebackendpkceandexchange)
21
+ - [authenticateBackendSmartStandard](#authenticatebackendsmartstandard)
22
+ - [getCachedBearerToken](#getcachedbearertoken)
23
+ - [Transport methods](#transport-methods)
24
+ - [submitBatch](#submitbatch)
25
+ - [submitBatchEncrypted](#submitbatchencrypted)
26
+ - [postJson](#postjson)
27
+ - [postFormData](#postformdata)
28
+ - [uploadConversionFile](#uploadconversionfile)
29
+ - [pollBatchResponse](#pollbatchresponse)
30
+ - [submitAndPoll](#submitandpoll)
31
+ - [pollUntilComplete](#polluntilcomplete)
32
+ - [High-level helpers](#high-level-helpers)
33
+ - [createPhoneReminderTasks](#createphoneremiindertasks)
34
+ - [searchFamilyOrganization](#searchfamilyorganization)
35
+ - [Standalone exports](#standalone-exports)
36
+
37
+ ---
38
+
39
+ ## Setup
40
+
41
+ ```bash
42
+ npm install
43
+ npm run build
44
+ ```
45
+
46
+ ```ts
47
+ import { DataspaceNodeClient, createDidcommPlainMessage } from 'dataspace-client-sdk-node';
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Types
53
+
54
+ ### `RouteContext`
55
+
56
+ Tenant-scoped routing context required by most path helpers and high-level methods.
57
+
58
+ ```ts
59
+ type RouteContext = {
60
+ tenantId: string; // e.g. 'acme'
61
+ jurisdiction: string; // ISO-3166 alpha-2, e.g. 'ES'
62
+ sector: string; // e.g. 'health-care', 'animal-care'
63
+ };
64
+ ```
65
+
66
+ ### `HostRouteContext`
67
+
68
+ Used for host/controller-level registry paths (no `tenantId`).
69
+
70
+ ```ts
71
+ type HostRouteContext = {
72
+ jurisdiction: string;
73
+ sector: string;
74
+ };
75
+ ```
76
+
77
+ ### `SubmitResponse`
78
+
79
+ ```ts
80
+ type SubmitResponse = {
81
+ status: number;
82
+ location?: string; // async poll URL from Location header
83
+ body: unknown;
84
+ };
85
+ ```
86
+
87
+ ### `PollResult`
88
+
89
+ ```ts
90
+ type PollResult = {
91
+ status: number;
92
+ body: unknown;
93
+ attempts: number;
94
+ };
95
+ ```
96
+
97
+ ### `SubmitAndPollResult`
98
+
99
+ ```ts
100
+ type SubmitAndPollResult = {
101
+ submit: SubmitResponse;
102
+ poll: PollResult;
103
+ };
104
+ ```
105
+
106
+ ### `PollOptions`
107
+
108
+ ```ts
109
+ type PollOptions = {
110
+ timeoutMs?: number; // default 20000
111
+ intervalMs?: number; // default 1000
112
+ };
113
+ ```
114
+
115
+ ### `FamilyRegistrationStatus`
116
+
117
+ ```ts
118
+ type FamilyRegistrationStatus = 'new_created' | 'resume_required' | 'already_exists' | 'not_found';
119
+ ```
120
+
121
+ ### `FamilyOrganizationSummary`
122
+
123
+ ```ts
124
+ type FamilyOrganizationSummary = {
125
+ organizationId: string;
126
+ status: FamilyRegistrationStatus;
127
+ controllerPhone?: string;
128
+ nickname?: string;
129
+ notificationPhone?: string;
130
+ sector?: string;
131
+ jurisdiction?: string;
132
+ };
133
+ ```
134
+
135
+ ### `CreatePhoneReminderTasksInput`
136
+
137
+ ```ts
138
+ type CreatePhoneReminderTasksInput = {
139
+ windows: Array<{ offsetMinutes: number; remindAt: string }>; // remindAt = ISO-8601
140
+ locale?: string;
141
+ callSid?: string;
142
+ notificationPhone?: string;
143
+ controllerPhone?: string;
144
+ subjectRef: string; // e.g. 'Person/+34600000001'
145
+ ownerRef: string; // e.g. 'RelatedPerson/+34699999999'
146
+ focusRef: string; // e.g. 'Appointment/2026-05-10T10:00:00.000Z'
147
+ reminderSummary?: string;
148
+ description?: string;
149
+ maxAttempts?: number;
150
+ dataType?: string;
151
+ };
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Constructor
157
+
158
+ ```ts
159
+ new DataspaceNodeClient(options: ClientOptions)
160
+ ```
161
+
162
+ | Option | Type | Required | Description |
163
+ |---|---|---|---|
164
+ | `baseUrl` | `string` | ✅ | Base URL of the GW/UNID API, e.g. `http://localhost:3000` |
165
+ | `bearerToken` | `string` | | Static bearer token for `Authorization` header |
166
+ | `defaultHeaders` | `Record<string, string>` | | Extra headers added to every request |
167
+ | `wallet` | `WalletProvider` | | Required only for `submitBatchEncrypted` and `authenticateBackendPkceAndExchange` with JWK provisioning |
168
+
169
+ ```ts
170
+ const client = new DataspaceNodeClient({
171
+ baseUrl: 'http://localhost:3000',
172
+ bearerToken: 'my-api-key',
173
+ });
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Path helpers
179
+
180
+ Path helpers return URL strings. They do **not** make network requests.
181
+ Every path has a submit path (`_batch` / `_search`) and a matching poll path (`_batch-response` / `_search-response`).
182
+
183
+ ### Generic builders
184
+
185
+ #### `v1Path(ctx, section, format, resourceType, action): string`
186
+
187
+ Builds any GW v1 route. Use when no dedicated helper exists.
188
+
189
+ ```
190
+ /{tenantId}/cds-{jurisdiction}/v1/{sector}/{section}/{format}/{resourceType}/{action}
191
+ ```
192
+
193
+ ```ts
194
+ client.v1Path(ctx, 'individual', 'org.schema', 'Organization', '_batch')
195
+ // → /acme/cds-ES/v1/health-care/individual/org.schema/Organization/_batch
196
+ ```
197
+
198
+ #### `tenantIdentityPath(ctx, prefix, action): string`
199
+
200
+ Builds identity/auth routes scoped to a service prefix (`host`, `publisher`, `ica`).
201
+
202
+ ```
203
+ /{prefix}/cds-{jurisdiction}/v1/{sector}/{tenantId}/identity/auth/{action}
204
+ ```
205
+
206
+ #### `hostRegistryPath(ctx, resourceType, action): string`
207
+
208
+ Builds host-level registry routes (no `tenantId`).
209
+
210
+ ```
211
+ /host/cds-{jurisdiction}/v1/{sector}/registry/org.schema/{resourceType}/{action}
212
+ ```
213
+
214
+ ---
215
+
216
+ ### Host registry (controller-level)
217
+
218
+ | Method | Returns | Route |
219
+ |---|---|---|
220
+ | `hostRegistryOrganizationBatchPath(ctx)` | submit path | `/host/.../registry/org.schema/Organization/_batch` |
221
+ | `hostRegistryOrganizationPollPath(ctx)` | poll path | `.../_batch-response` |
222
+ | `hostRegistryOrganizationActivatePath(ctx)` | submit path | `.../_activate` |
223
+ | `hostRegistryOrganizationActivatePollPath(ctx)` | poll path | `.../_activate-response` |
224
+ | `hostRegistryOrderBatchPath(ctx)` | submit path | `.../Order/_batch` |
225
+ | `hostRegistryOrderPollPath(ctx)` | poll path | `.../Order/_batch-response` |
226
+
227
+ ```ts
228
+ const ctx: HostRouteContext = { jurisdiction: 'ES', sector: 'health-care' };
229
+
230
+ const result = await client.submitAndPoll(
231
+ client.hostRegistryOrganizationBatchPath(ctx),
232
+ client.hostRegistryOrganizationPollPath(ctx),
233
+ payload,
234
+ );
235
+ ```
236
+
237
+ ---
238
+
239
+ ### Individual — Family Organization
240
+
241
+ | Method | Returns | Route |
242
+ |---|---|---|
243
+ | `individualFamilyOrganizationBatchPath(ctx)` | submit | `.../individual/org.schema/Organization/_batch` |
244
+ | `individualFamilyOrganizationPollPath(ctx)` | poll | `.../_batch-response` |
245
+ | `individualFamilyOrganizationSearchPath(ctx)` | submit | `.../Organization/_search` |
246
+ | `individualFamilyOrganizationSearchPollPath(ctx)` | poll | `.../Organization/_search-response` |
247
+ | `individualFamilyOrderBatchPath(ctx)` | submit | `.../org.schema/Order/_batch` |
248
+ | `individualFamilyOrderPollPath(ctx)` | poll | `.../Order/_batch-response` |
249
+
250
+ ```ts
251
+ const ctx: RouteContext = { tenantId: 'acme', jurisdiction: 'ES', sector: 'health-care' };
252
+
253
+ // Register or resume a family organization
254
+ const payload = createDidcommPlainMessage({
255
+ iss: '+34600000001',
256
+ aud: 'did:web:api.acme.org',
257
+ body: {
258
+ data: [{
259
+ type: 'Family-registration-form-v1.0',
260
+ meta: {
261
+ claims: {
262
+ '@context': 'org.schema',
263
+ 'org.schema.Organization.telephone': '+34611111111', // notification phone (subject)
264
+ 'org.schema.Organization.creator': '+34600000001', // controller phone
265
+ 'org.schema.Organization.owner.telephone': '+34600000001', // controller (indexed)
266
+ 'org.schema.Organization.alternateName': 'Maria', // usualname (indexed)
267
+ 'org.schema.Service.category': 'health-care',
268
+ 'org.schema.Organization.addressCountry': 'ES',
269
+ 'org.schema.Organization.identifierValue': 'f0d4b66d-7e28-4fa2-91b7-7041e57f4f90',
270
+ '@type': 'receipt',
271
+ },
272
+ },
273
+ }],
274
+ },
275
+ });
276
+
277
+ const result = await client.submitAndPoll(
278
+ client.individualFamilyOrganizationBatchPath(ctx),
279
+ client.individualFamilyOrganizationPollPath(ctx),
280
+ payload,
281
+ );
282
+ // result.poll.body contains 'org.schema.FamilyRegistration.status': 'new_created' | 'already_exists' | 'resume_required'
283
+ ```
284
+
285
+ > Prefer the high-level [`searchFamilyOrganization`](#searchfamilyorganization) instead of calling the search path manually.
286
+
287
+ ---
288
+
289
+ ### Individual — FHIR resources
290
+
291
+ | Method | Returns | Route |
292
+ |---|---|---|
293
+ | `individualRelatedPersonBatchPath(ctx)` | submit | `.../individual/org.hl7.fhir.api/RelatedPerson/_batch` |
294
+ | `individualRelatedPersonPollPath(ctx)` | poll | `.../_batch-response` |
295
+ | `individualObservationBatchPath(ctx)` | submit | `.../org.hl7.fhir.api/Observation/_batch` |
296
+ | `individualObservationPollPath(ctx)` | poll | `.../_batch-response` |
297
+ | `individualCommunicationBatchPath(ctx)` | submit | `.../org.hl7.fhir.r4/Communication/_batch` |
298
+ | `individualCommunicationPollPath(ctx)` | poll | `.../_batch-response` |
299
+ | `individualTaskBatchPath(ctx)` | submit | `.../org.hl7.fhir.api/Task/_batch` |
300
+ | `individualTaskPollPath(ctx)` | poll | `.../_batch-response` |
301
+ | `individualConsentR4BatchPath(ctx)` | submit | `.../org.hl7.fhir.r4/Consent/_batch` |
302
+ | `individualConsentR4PollPath(ctx)` | poll | `.../_batch-response` |
303
+ | `individualCompositionR4BatchPath(ctx)` | submit | `.../org.hl7.fhir.r4/Composition/_batch` |
304
+ | `individualCompositionR4PollPath(ctx)` | poll | `.../_batch-response` |
305
+ | `individualLegacyPersonBatchPath(ctx)` | submit | `.../org.schema/Person/_batch` (legacy) |
306
+ | `digitalTwinCompositionApiBatchPath(ctx)` | submit | `.../digitaltwin/org.hl7.fhir.api/Composition/_batch` |
307
+ | `digitalTwinCompositionApiPollPath(ctx)` | poll | `.../_batch-response` |
308
+ | `digitalTwinCompositionR4BatchPath(ctx)` | submit | `.../digitaltwin/org.hl7.fhir.r4/Composition/_batch` |
309
+ | `digitalTwinCompositionR4PollPath(ctx)` | poll | `.../_batch-response` |
310
+
311
+ ```ts
312
+ // Submit a FHIR Task
313
+ const taskPayload = createDidcommPlainMessage({
314
+ iss: ctx.tenantId,
315
+ aud: ctx.tenantId,
316
+ body: {
317
+ data: [{
318
+ type: 'Task',
319
+ request: { method: 'POST' },
320
+ resource: {
321
+ resourceType: 'Task',
322
+ id: 'task-abc',
323
+ description: 'Medication reminder',
324
+ meta: {
325
+ claims: {
326
+ status: 'scheduled',
327
+ subject: 'Person/+34600000001',
328
+ channel: 'phone',
329
+ 'trigger-type': 'phone-call',
330
+ 'execution-period-start': '2026-05-10T09:00:00.000Z',
331
+ },
332
+ },
333
+ },
334
+ }],
335
+ },
336
+ });
337
+
338
+ const result = await client.submitAndPoll(
339
+ client.individualTaskBatchPath(ctx),
340
+ client.individualTaskPollPath(ctx),
341
+ taskPayload,
342
+ );
343
+ ```
344
+
345
+ ---
346
+
347
+ ### Entity — Employee
348
+
349
+ | Method | Returns | Route |
350
+ |---|---|---|
351
+ | `employeeBatchPath(ctx)` | submit | `.../entity/org.schema/Employee/_batch` |
352
+ | `employeePollPath(ctx)` | poll | `.../_batch-response` |
353
+
354
+ ```ts
355
+ const result = await client.submitAndPoll(
356
+ client.employeeBatchPath(ctx),
357
+ client.employeePollPath(ctx),
358
+ employeePayload,
359
+ );
360
+ ```
361
+
362
+ ---
363
+
364
+ ### Identity / Auth
365
+
366
+ These are used internally by [`authenticateBackendPkceAndExchange`](#authenticatebackendpkceandexchange). Exposed for custom flows.
367
+
368
+ | Method | Route |
369
+ |---|---|
370
+ | `identityDeviceDcrPath(ctx)` | `host/.../identity/auth/_dcr` |
371
+ | `identityDeviceDcrPollPath(ctx)` | `.../_dcr-response` |
372
+ | `identityCodePath(ctx)` | `.../_code` |
373
+ | `identityCodePollPath(ctx)` | `.../_code-response` |
374
+ | `identitySmartTokenPath(ctx)` | `.../_token` |
375
+ | `identitySmartTokenPollPath(ctx)` | `.../_token-response` |
376
+ | `identityTokenExchangePath(ctx)` | `.../_exchange` |
377
+ | `identityTokenExchangePollPath(ctx)` | `.../_exchange-response` |
378
+ | `identityLicenseIssuePath(ctx)` | `.../_issue` |
379
+ | `identityFirebaseCustomPath(ctx)` | `.../_custom` |
380
+ | `identityFirebaseCustomPollPath(ctx)` | `.../_custom-response` |
381
+
382
+ ---
383
+
384
+ ### DataConversion upload
385
+
386
+ | Method | Route |
387
+ |---|---|
388
+ | `conversionUploadPath(ctx, softwareId, sourceFormat)` | `.../conversion/{softwareId}/{sourceFormat}/_upload` |
389
+ | `conversionUploadPollPath(ctx, softwareId, sourceFormat)` | `.../_upload-response` |
390
+
391
+ ```ts
392
+ const path = client.conversionUploadPath(ctx, 'qvet', 'xlsx');
393
+ // → /acme/cds-ES/v1/health-care/conversion/qvet/xlsx/_upload
394
+ ```
395
+
396
+ ---
397
+
398
+ ### Task debug
399
+
400
+ | Method | Route |
401
+ |---|---|
402
+ | `taskDebugCallStartPath(ctx, format?)` | `.../individual/{format}/Task/_call-start` |
403
+ | `taskDebugLogsPath(ctx, format?)` | `.../individual/{format}/Task/_logs` |
404
+
405
+ `format` defaults to `'org.hl7.fhir.api'`.
406
+
407
+ ```ts
408
+ // Trigger a debug voice call for a scheduled task
409
+ await client.postJson(client.taskDebugCallStartPath(ctx), { thid: 'task-abc123' });
410
+
411
+ // Poll logs
412
+ const logs = await client.postJson(client.taskDebugLogsPath(ctx), { thid: 'task-abc123' });
413
+ ```
414
+
415
+ ---
416
+
417
+ ## Auth methods
418
+
419
+ ### `authenticateBackendPkceAndExchange`
420
+
421
+ Orchestrates the full identity-exchange.v1 backend auth flow:
422
+ **DCR → PKCE code → SMART token → bearer exchange**.
423
+
424
+ Tokens are cached in memory. Automatically refreshes on expiry.
425
+
426
+ ```ts
427
+ const result = await client.authenticateBackendPkceAndExchange({
428
+ ctx: { tenantId: 'acme', jurisdiction: 'ES', sector: 'health-care' },
429
+ apiKey: 'my-api-key',
430
+ scopes: ['organization/Task.cruds', 'organization/Observation.rs'],
431
+ // Optional: provide your own wallet for JWK signing
432
+ // walletContext: { ... },
433
+ });
434
+
435
+ if (result.status === 'fetched' || result.status === 'cached') {
436
+ console.log(result.accessToken); // use as Bearer
437
+ }
438
+ ```
439
+
440
+ **Returns** `BackendPkceAuthResult`:
441
+
442
+ ```ts
443
+ type BackendPkceAuthResult =
444
+ | { status: 'fetched' | 'cached'; endpointId: string; accessToken: string; tokenType: string; scopes: string[] }
445
+ | { status: 'failed'; step: '_dcr' | '_code' | '_token' | '_exchange'; endpointId: string; accessToken: ''; tokenType: 'Bearer'; scopes: string[] };
446
+ ```
447
+
448
+ ---
449
+
450
+ ### `authenticateBackendSmartStandard`
451
+
452
+ OAuth2 `client_credentials` flow using `private_key_jwt` assertion (SMART Backend Services profile).
453
+
454
+ ```ts
455
+ const result = await client.authenticateBackendSmartStandard({
456
+ ctx: { tenantId: 'acme', jurisdiction: 'ES', sector: 'health-care' },
457
+ clientId: 'backend-service-client-id',
458
+ scopes: ['system/Task.rs'],
459
+ tokenUrl: 'https://auth.acme.org/token',
460
+ });
461
+
462
+ if (result.status === 'fetched' || result.status === 'cached') {
463
+ console.log(result.accessToken);
464
+ }
465
+ ```
466
+
467
+ ---
468
+
469
+ ### `getCachedBearerToken`
470
+
471
+ Returns the cached bearer token for an `endpointId` if valid (>30s remaining), or `undefined`.
472
+
473
+ ```ts
474
+ const token = client.getCachedBearerToken('pkce:my-api-key');
475
+ ```
476
+
477
+ ---
478
+
479
+ ## Transport methods
480
+
481
+ ### `submitBatch`
482
+
483
+ POST a DIDComm plaintext payload (`application/didcomm-plaintext+json`).
484
+
485
+ ```ts
486
+ submitBatch(path: string, payload: unknown): Promise<SubmitResponse>
487
+ ```
488
+
489
+ ```ts
490
+ const { status, location } = await client.submitBatch(
491
+ client.individualTaskBatchPath(ctx),
492
+ payload,
493
+ );
494
+ // location header contains the poll URL
495
+ ```
496
+
497
+ ---
498
+
499
+ ### `submitBatchEncrypted`
500
+
501
+ Sign and encrypt a DIDComm payload as JWS-in-JWE, then POST it with `Content-Type: application/didcomm-encrypted+json`.
502
+
503
+ Requires a configured `WalletProvider` in the client constructor.
504
+
505
+ ```ts
506
+ submitBatchEncrypted(
507
+ path: string,
508
+ payload: Record<string, unknown>,
509
+ recipientEncryptionJwk: PublicJwk,
510
+ walletContext: WalletContext,
511
+ ): Promise<SubmitResponse>
512
+ ```
513
+
514
+ ```ts
515
+ // recipientEncryptionJwk from GW /.well-known/jwks.json where use === 'enc'
516
+ const response = await client.submitBatchEncrypted(
517
+ client.individualFamilyOrganizationBatchPath(ctx),
518
+ payload,
519
+ recipientJwk,
520
+ walletCtx,
521
+ );
522
+ ```
523
+
524
+ ---
525
+
526
+ ### `postJson`
527
+
528
+ POST a plain JSON body (`application/json`). Use for token endpoints and API key management.
529
+
530
+ ```ts
531
+ postJson(path: string, payload: unknown): Promise<SubmitResponse>
532
+ ```
533
+
534
+ ```ts
535
+ const response = await client.postJson('/host/admin/api-keys', {
536
+ agent: { email: 'service@example.com' },
537
+ scope: ['organization/Task.cruds'],
538
+ });
539
+ ```
540
+
541
+ ---
542
+
543
+ ### `postFormData`
544
+
545
+ POST a `multipart/form-data` payload. Prefer `uploadConversionFile` for file uploads.
546
+
547
+ ```ts
548
+ postFormData(path: string, formData: FormData): Promise<SubmitResponse>
549
+ ```
550
+
551
+ ---
552
+
553
+ ### `uploadConversionFile`
554
+
555
+ Upload a file to a DataConversion endpoint. Wraps `postFormData`.
556
+
557
+ ```ts
558
+ uploadConversionFile(params: {
559
+ path: string;
560
+ fileName: string;
561
+ fileContent: Blob | Buffer | Uint8Array | ArrayBuffer;
562
+ fileFieldName?: string; // default 'file'
563
+ fields?: Record<string, string>;
564
+ }): Promise<SubmitResponse>
565
+ ```
566
+
567
+ ```ts
568
+ import { readFileSync } from 'node:fs';
569
+
570
+ const fileBytes = readFileSync('patients.xlsx');
571
+ const response = await client.uploadConversionFile({
572
+ path: client.conversionUploadPath(ctx, 'qvet', 'xlsx'),
573
+ fileName: 'patients.xlsx',
574
+ fileContent: fileBytes,
575
+ });
576
+ ```
577
+
578
+ ---
579
+
580
+ ### `pollBatchResponse`
581
+
582
+ Poll a `_batch-response` / `_search-response` path with a `thid`. Returns raw status + body.
583
+
584
+ ```ts
585
+ pollBatchResponse(path: string, request: AsyncPollRequest): Promise<{ status: number; body: unknown }>
586
+ ```
587
+
588
+ ```ts
589
+ const { status, body } = await client.pollBatchResponse(
590
+ client.individualTaskPollPath(ctx),
591
+ { thid: 'task-abc123' },
592
+ );
593
+ ```
594
+
595
+ ---
596
+
597
+ ### `submitAndPoll`
598
+
599
+ Combines `submitBatch` + `pollUntilComplete` in a single call. The most common transport pattern.
600
+
601
+ ```ts
602
+ submitAndPoll(
603
+ submitPath: string,
604
+ pollPath: string,
605
+ payload: unknown,
606
+ options?: PollOptions,
607
+ ): Promise<SubmitAndPollResult>
608
+ ```
609
+
610
+ Default poll options: `timeoutMs: 20_000`, `intervalMs: 1_000`.
611
+
612
+ ```ts
613
+ const result = await client.submitAndPoll(
614
+ client.individualFamilyOrganizationBatchPath(ctx),
615
+ client.individualFamilyOrganizationPollPath(ctx),
616
+ payload,
617
+ { timeoutMs: 30_000, intervalMs: 500 },
618
+ );
619
+
620
+ console.log(result.submit.status); // 202
621
+ console.log(result.poll.status); // 200
622
+ console.log(result.poll.body);
623
+ ```
624
+
625
+ ---
626
+
627
+ ### `pollUntilComplete`
628
+
629
+ Polls a path until the server returns a non-202 status, or timeout is reached.
630
+
631
+ ```ts
632
+ pollUntilComplete(
633
+ path: string,
634
+ request: AsyncPollRequest,
635
+ options?: PollOptions,
636
+ ): Promise<PollResult>
637
+ ```
638
+
639
+ ```ts
640
+ const poll = await client.pollUntilComplete(
641
+ client.individualTaskPollPath(ctx),
642
+ { thid: 'task-abc123' },
643
+ { timeoutMs: 15_000 },
644
+ );
645
+ console.log(poll.status, poll.attempts);
646
+ ```
647
+
648
+ ---
649
+
650
+ ## High-level helpers
651
+
652
+ ### `createPhoneReminderTasks`
653
+
654
+ Creates one reminder `Task` per reminder window via `individual/Task/_batch`.
655
+
656
+ Each window maps to one deterministic task ID (SHA-256 of routing context + refs + `remindAt`).
657
+
658
+ ```ts
659
+ createPhoneReminderTasks(
660
+ ctx: RouteContext,
661
+ input: CreatePhoneReminderTasksInput,
662
+ options?: PollOptions,
663
+ ): Promise<SubmitAndPollResult>
664
+ ```
665
+
666
+ ```ts
667
+ const result = await client.createPhoneReminderTasks(ctx, {
668
+ windows: [
669
+ { offsetMinutes: 10080, remindAt: '2026-05-03T10:00:00.000Z' }, // 1 week before
670
+ { offsetMinutes: 1440, remindAt: '2026-05-09T10:00:00.000Z' }, // 1 day before
671
+ { offsetMinutes: 60, remindAt: '2026-05-10T09:00:00.000Z' }, // 1 hour before
672
+ ],
673
+ locale: 'es-ES',
674
+ notificationPhone: '+34611111111',
675
+ controllerPhone: '+34600000001',
676
+ subjectRef: 'Person/+34611111111',
677
+ ownerRef: 'RelatedPerson/+34600000001',
678
+ focusRef: 'Appointment/2026-05-10T10:00:00.000Z',
679
+ reminderSummary: '10/05 10:00 | Hospital San Juan | Consulta cardiología',
680
+ maxAttempts: 3,
681
+ });
682
+
683
+ console.log(result.poll.status); // 200 if all tasks created
684
+ ```
685
+
686
+ ---
687
+
688
+ ### `searchFamilyOrganization`
689
+
690
+ Search for an existing family organization by controller phone + usualname. Returns `null` when not found.
691
+
692
+ ```ts
693
+ searchFamilyOrganization(
694
+ ctx: RouteContext,
695
+ filters: { controllerPhone: string; usualname: string; birthDate?: string },
696
+ options?: PollOptions,
697
+ ): Promise<FamilyOrganizationSummary | null>
698
+ ```
699
+
700
+ ```ts
701
+ const org = await client.searchFamilyOrganization(ctx, {
702
+ controllerPhone: '+34600000001',
703
+ usualname: 'Maria',
704
+ });
705
+
706
+ if (org === null) {
707
+ // not found → proceed with registration
708
+ } else if (org.status === 'already_exists') {
709
+ console.log(org.organizationId); // UUID of the existing org
710
+ } else if (org.status === 'resume_required') {
711
+ // pending → re-submit Order to activate
712
+ }
713
+ ```
714
+
715
+ ---
716
+
717
+ ## Standalone exports
718
+
719
+ ### `createDidcommPlainMessage`
720
+
721
+ Builds a DIDComm plaintext message object with a random `jti` and `thid`.
722
+
723
+ ```ts
724
+ import { createDidcommPlainMessage } from 'dataspace-client-sdk-node';
725
+
726
+ const payload = createDidcommPlainMessage({
727
+ iss: 'acme',
728
+ aud: 'did:web:api.acme.org',
729
+ body: {
730
+ data: [{ type: 'Task', resource: { ... } }],
731
+ },
732
+ });
733
+ // payload.jti, payload.thid are set automatically
734
+ ```
735
+
736
+ ```ts
737
+ createDidcommPlainMessage(params: {
738
+ iss: string;
739
+ aud: string;
740
+ type?: string; // default 'didcomm-plain'
741
+ thid?: string; // auto-generated UUID if omitted
742
+ body: Record<string, unknown>;
743
+ meta?: Record<string, unknown>;
744
+ }): DidcommPlainMessage
745
+ ```