@healthcloudai/hc-safe-cdx 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -1,17 +1,10 @@
1
1
  # Healthcheck Safe CDX Connector
2
2
 
3
- Safe CDX connector provides a thin TypeScript wrapper around Safe CDX API routes.
3
+ Safe CDX supports the diagnostic test workflow performed by an authenticated patient. It provides the SDK methods used to retrieve available test profiles, resolve a selected test by GTIN, prepare and upload test images, process test results, and complete the test flow.
4
4
 
5
- The connector follows the same shared-client pattern used by other Health Cloud connector modules:
5
+ `HCSafeCDXClient` uses the authenticated patient context provided by `HCLoginClient`. The client derives its own Safe CDX service URL from the configured environment, sends authenticated requests to Safe CDX routes, and wraps request payloads internally where the API expects the standard `Data` envelope.
6
6
 
7
- - `HttpClient` handles HTTP transport.
8
- - `HCLoginClient` provides the authenticated request headers.
9
- - `HCSafeCDXClient` builds Safe CDX URLs, wraps request payloads, and exposes one method per Safe CDX route.
10
-
11
- Safe CDX has its own API host. The connector uses `HCLoginClient` for authentication only; it does not use `loginClient.getBaseUrl()` for Safe CDX routes.
12
- Like `HCHealthRecordClient`, `HCSafeCDXClient` reads the configured environment from `HCLoginClient` and derives its own service base URL internally.
13
-
14
- Image upload is handled separately because the upload target is a pre-signed S3 URL, not a Safe CDX API route.
7
+ Image upload is handled separately because the upload target is a pre-signed storage URL rather than a Safe CDX API route.
15
8
 
16
9
  ## Installation
17
10
 
@@ -27,358 +20,323 @@ import { HCLoginClient } from "@healthcloudai/hc-login-connector";
27
20
  import { HCSafeCDXClient } from "@healthcloudai/hc-safe-cdx";
28
21
  ```
29
22
 
30
- ## Basic Setup
31
-
32
- ```ts
33
- const http = new FetchClient();
34
-
35
- const loginClient = new HCLoginClient(http);
36
-
37
- loginClient.configure("healthcheck", "dev");
38
- await loginClient.login(email, password);
39
-
40
- const safeCdx = new HCSafeCDXClient(http, loginClient);
41
- ```
42
-
43
- The connector does not perform login. The consuming application should authenticate through the Health Cloud login connector first. Safe CDX then reuses the authenticated headers from `loginClient.getAuthHeader()`.
44
-
45
- There is no `setAccessToken()` step in this version of the client.
46
-
47
- ## Dependency Notes
48
-
49
- Because `HCSafeCDXClient` accepts `HCLoginClient` in the constructor, `@healthcloudai/hc-login-connector` should be included as a package dependency.
50
-
51
- Example package configuration:
52
-
53
- ```json
54
- {
55
- "dependencies": {
56
- "@healthcloudai/hc-http": "^0.x.x",
57
- "@healthcloudai/hc-login-connector": "^0.x.x"
58
- }
59
- }
60
- ```
61
-
62
- In a workspace setup, use the same dependency versioning strategy already used by the other connector packages.
23
+ ## Setup
63
24
 
64
- Example:
65
-
66
- ```json
67
- {
68
- "dependencies": {
69
- "@healthcloudai/hc-http": "workspace:*",
70
- "@healthcloudai/hc-login-connector": "workspace:*"
71
- }
72
- }
73
- ```
74
-
75
- ## How the Client Works
76
-
77
- The client receives both the HTTP client and the login client:
25
+ Create the shared HTTP client, authenticate the patient through `HCLoginClient`, and then create the Safe CDX client with the authenticated login instance.
78
26
 
79
27
  ```ts
80
- const http = new FetchClient();
81
- const loginClient = new HCLoginClient(http);
28
+ const httpClient = new FetchClient();
29
+ const loginClient = new HCLoginClient(httpClient);
82
30
 
83
31
  loginClient.configure("healthcheck", "dev");
84
32
  await loginClient.login(email, password);
85
33
 
86
- const safeCdx = new HCSafeCDXClient(http, loginClient);
34
+ const safeCdx = new HCSafeCDXClient(httpClient, loginClient);
87
35
  ```
88
36
 
89
- The shared `HttpClient` is responsible for executing HTTP requests.
37
+ The Safe CDX connector does not perform login and does not require a separate `setAccessToken(...)` step. Authenticated headers are read from the existing `HCLoginClient` instance.
90
38
 
91
- The `HCLoginClient` is responsible for the authenticated request headers.
39
+ ## Client Configuration
92
40
 
93
- The Safe CDX client is responsible for:
41
+ ### Service URL
94
42
 
95
- - deriving the Safe CDX base URL from the login client's configured environment
96
- - building Safe CDX URLs
97
- - adding auth headers from `HCLoginClient`
98
- - adding optional API key headers when configured
99
- - wrapping POST request payloads into `{ Data: payload }`
100
- - validating `ApiResponse` results where the backend returns `{ IsOK, Data, ErrorMessage }`
101
- - exposing one SDK method per Safe CDX route
43
+ Safe CDX uses its own service base URL and does not reuse `loginClient.getBaseUrl()`. The client reads the configured environment through `loginClient.getEnvironment()` and resolves the Safe CDX host internally.
102
44
 
103
- The Safe CDX client does not create its own internal `fetch` helpers for standard Safe CDX API routes.
104
45
 
105
- ## Base URL Configuration
46
+ ### Optional API Key
106
47
 
107
- Safe CDX base URL configuration follows the same constructor pattern as the Health Record connector:
48
+ When an environment requires an API key header, configure it on the Safe CDX client:
108
49
 
109
50
  ```ts
110
- loginClient.configure("healthcheck", "dev");
111
-
112
- const safeCdx = new HCSafeCDXClient(http, loginClient);
113
- ```
114
-
115
- The Safe CDX client reads the environment from:
116
-
117
- ```ts
118
- loginClient.getEnvironment()
119
- ```
120
-
121
- Safe CDX still uses its own service base URL. It does not reuse `loginClient.getBaseUrl()`.
122
-
123
- Environment mapping is handled internally by the connector:
124
-
125
- ```txt
126
- dev -> https://dev-api-hcs.healthcloud-services.com/api/console/hcservice/safecdx
127
- uat -> https://uat-api-hcs.healthcloud-services.com/api/console/hcservice/safecdx
128
- prod -> https://api-hcs.healthcloud-services.com/api/console/hcservice/safecdx
51
+ safeCdx.setApiKey("x-api-key", apiKeyValue);
129
52
  ```
130
53
 
131
- These base URLs are separate from the login connector base URL.
54
+ The configured API key header is added to Safe CDX API route requests together with the patient authentication headers.
132
55
 
133
- ## Authentication
56
+ ## Request Contract
134
57
 
135
- Safe CDX API calls require authenticated Health Cloud headers.
58
+ SDK methods receive only the values needed by the consuming application. The consumer does not manually provide the backend `Data` wrapper, tenant context, patient identity values, or authentication headers.
136
59
 
137
- The connector gets those headers from:
60
+ For POST requests, `HCSafeCDXClient` constructs the API request envelope internally:
138
61
 
139
62
  ```ts
140
- loginClient.getAuthHeader()
63
+ interface APIRequest<T> {
64
+ Data: T;
65
+ }
141
66
  ```
142
67
 
143
- This means the login connector must be configured and authenticated before Safe CDX methods are called.
144
-
145
- Example flow:
68
+ For example:
146
69
 
147
70
  ```ts
148
- const http = new FetchClient();
149
-
150
- const loginClient = new HCLoginClient(http);
151
-
152
- loginClient.configure("healthcheck", "dev");
153
- await loginClient.login(email, password);
154
-
155
- const safeCdx = new HCSafeCDXClient(http, loginClient);
71
+ await safeCdx.submitAnswers({
72
+ UserTestResultId: userTestResultId,
73
+ Result: [
74
+ {
75
+ Analyte: "Purchase",
76
+ ReportedValue: "drug store",
77
+ StoredValue: "drug store",
78
+ Score: "0"
79
+ }
80
+ ]
81
+ });
156
82
  ```
157
83
 
158
- Safe CDX does not manually receive a token. It does not expose `setAccessToken()`. Auth is inherited from the configured login client.
159
-
160
- ## Optional API Key Header
84
+ The client sends:
161
85
 
162
- If an API key header is required by the environment, it can be set on the Safe CDX client:
163
-
164
- ```ts
165
- safeCdx.setApiKey("x-api-key", apiKeyValue);
86
+ ```json
87
+ {
88
+ "Data": {
89
+ "UserTestResultId": "<USER_TEST_RESULT_ID>",
90
+ "Result": [
91
+ {
92
+ "Analyte": "Purchase",
93
+ "ReportedValue": "drug store",
94
+ "StoredValue": "drug store",
95
+ "Score": "0"
96
+ }
97
+ ]
98
+ }
99
+ }
166
100
  ```
167
101
 
168
- When configured, the API key header is added to Safe CDX API calls together with the login auth headers.
169
-
170
- ## Request Payload Wrapping
102
+ ## Response Contract
171
103
 
172
- For POST requests, only the inner payload should be passed to SDK methods.
104
+ Safe CDX methods return the response shape produced by their backend endpoint without unwrapping or transforming it.
173
105
 
174
- The SDK wraps the payload internally as:
106
+ Most documented methods return the standard Health Cloud response envelope:
175
107
 
176
108
  ```ts
177
- {
178
- Data: payload
109
+ interface APIResponse<T> {
110
+ Data: T | null;
111
+ IsOK: boolean;
112
+ ErrorMessage: string | null;
179
113
  }
180
114
  ```
181
115
 
182
- Correct:
116
+ Several Safe CDX operations return a service response inside `APIResponse.Data`, or return that service response directly:
183
117
 
184
118
  ```ts
185
- await safeCdx.submitAnswers({
186
- UserTestResultId: userTestResultId,
187
- Result: [
188
- {
189
- Analyte: "Purchase",
190
- ReportedValue: "drug store",
191
- StoredValue: "drug store",
192
- Score: "0",
193
- },
194
- ],
195
- });
119
+ interface SafeAPIResponse<T> {
120
+ success: boolean;
121
+ data: T | null;
122
+ message: string | null;
123
+ code: number;
124
+ }
196
125
  ```
197
126
 
198
- Incorrect:
127
+ The following methods return `SafeAPIResponse<T>` directly, without the outer `APIResponse<T>` wrapper:
199
128
 
200
129
  ```ts
201
- await safeCdx.submitAnswers({
202
- Data: {
203
- UserTestResultId: userTestResultId,
204
- Result: [],
205
- },
206
- });
130
+ safeCdx.updateCvmlStatus(...)
131
+ safeCdx.getCvmlResults(...)
132
+ safeCdx.getResultDetails(...)
133
+ safeCdx.getImageCaptureUrl(...)
207
134
  ```
208
135
 
209
- The caller should not manually add the `Data` wrapper.
136
+ `HCSafeCDXClient` does not replace an API-defined response with a different return value. Local errors may still be thrown before or outside a Safe CDX API response, such as invalid API key configuration or a failed direct image upload. Transport-level handling for unsuccessful HTTP responses is determined by the supplied `HttpClient`.
210
137
 
211
- ## API Response Handling
138
+ ## Parameter Design
212
139
 
213
- Most Safe CDX endpoints return the standard backend response envelope:
140
+ Public methods follow the documented patient workflow request shapes.
214
141
 
215
142
  ```ts
216
- {
217
- Data: T;
218
- IsOK: boolean;
219
- ErrorMessage: string | null;
220
- }
143
+ safeCdx.getTestProfileByGTIN(gtin)
144
+ safeCdx.getTestProfilesByAccount(includeRegisterTestDetails?)
145
+ safeCdx.createUploadUrl(userTestResultId, gtin, imageType?)
146
+ safeCdx.updateCvmlStatus(imageOfCaptureId, cvmlStatus)
147
+ safeCdx.getCvmlResults(imageOfCaptureId)
148
+ safeCdx.getPendingResults(excludeStatus?)
149
+ safeCdx.getLastResults(excludeStatus?)
150
+ safeCdx.getTestHistory(excludeStatus?)
151
+ safeCdx.getResultDetails(userTestResultId)
152
+ safeCdx.getResultPdf(userTestResultId)
153
+ safeCdx.getImageCaptureUrl(userTestResultId)
154
+ safeCdx.resumeFlow(userTestResultId, resumed?)
155
+ safeCdx.finalizeTest(userTestResultId)
221
156
  ```
222
157
 
223
- For these endpoints, the client validates `IsOK`.
224
158
 
225
- If `IsOK` is `false`, the client throws `SafeCDXError`.
226
-
227
- This validation exists because an HTTP `200` response does not always mean the backend operation succeeded. The HTTP client validates the transport layer, while the Safe CDX client validates the API response envelope.
228
-
229
- For the following methods, the client returns the received response directly without applying `ApiResponse` validation:
159
+ ## Typical Patient Workflow
230
160
 
231
161
  ```ts
232
- updateCvmlStatus(...)
233
- getCvmlResults(...)
234
- getResultDetails(...)
235
- getImageCaptureUrl(...)
236
- ```
237
-
238
- ## Image Upload Handling
162
+ const profiles = await safeCdx.getTestProfilesByAccount();
239
163
 
240
- Image upload is different from normal Safe CDX API calls.
164
+ const profile = await safeCdx.getTestProfileByGTIN("850024942325");
241
165
 
242
- The method `createUploadUrl(...)` returns a pre-signed S3 URL. That URL is then used to upload the image file directly.
166
+ if (!profile.IsOK || !profile.Data) {
167
+ throw new Error(profile.ErrorMessage ?? "Unable to resolve the test profile.");
168
+ }
243
169
 
244
- ```ts
245
- await safeCdx.uploadImage(preSignedURL, imageBody, "image/jpeg");
246
- ```
170
+ const userTestResultId = profile.Data.ID;
171
+ const gtin = profile.Data.DiagnosticProfile?.TestInfo.gtin;
247
172
 
248
- This upload does not go through the shared `HttpClient`.
173
+ if (!userTestResultId || !gtin) {
174
+ throw new Error("The selected test profile does not include the required workflow identifiers.");
175
+ }
249
176
 
250
- The reason is that the shared `FetchClient.put()` is JSON-oriented and sends request bodies with `JSON.stringify(...)`. Image upload needs to send a raw `BodyInit`, such as a `Blob`, `File`, `ArrayBuffer`, or stream-compatible body.
177
+ const upload = await safeCdx.createUploadUrl(userTestResultId, gtin);
251
178
 
252
- Authorization headers should not be sent to the pre-signed URL. The pre-signed URL already contains temporary upload authorization.
179
+ if (!upload.IsOK || !upload.Data?.preSignedURL || !upload.Data.Metadata?.UploadId) {
180
+ throw new Error(upload.ErrorMessage ?? "Unable to prepare image upload.");
181
+ }
253
182
 
254
- ## Typical Safe CDX Flow
183
+ const preSignedURL = upload.Data.preSignedURL;
184
+ const imageOfCaptureId = upload.Data.Metadata.UploadId;
255
185
 
256
- A typical flow looks like this:
186
+ await safeCdx.uploadImage(preSignedURL, imageBody, "image/jpeg");
187
+ await safeCdx.updateCvmlStatus(imageOfCaptureId, "ImageProcessing");
257
188
 
258
- ```ts
259
- const http = new FetchClient();
189
+ const cvmlResults = await safeCdx.getCvmlResults(imageOfCaptureId);
260
190
 
261
- const loginClient = new HCLoginClient(http);
191
+ await safeCdx.submitAnswers({
192
+ UserTestResultId: userTestResultId,
193
+ Result: [
194
+ {
195
+ Analyte: "Purchase",
196
+ ReportedValue: "drug store",
197
+ StoredValue: "drug store",
198
+ Score: "0"
199
+ }
200
+ ]
201
+ });
262
202
 
263
- loginClient.configure("healthcheck", "dev");
264
- await loginClient.login(email, password);
203
+ await safeCdx.finalizeTest(userTestResultId);
265
204
 
266
- const safeCdx = new HCSafeCDXClient(http, loginClient);
205
+ const details = await safeCdx.getResultDetails(userTestResultId);
206
+ const pdf = await safeCdx.getResultPdf(userTestResultId);
207
+ const imageCaptureUrl = await safeCdx.getImageCaptureUrl(userTestResultId);
267
208
  ```
268
209
 
269
- ### 1. Get available test profiles
210
+ The application is responsible for preserving workflow values returned between steps, including `userTestResultId`, `gtin`, `preSignedURL`, and `imageOfCaptureId`.
270
211
 
271
- ```ts
272
- const profiles = await safeCdx.getTestProfilesByAccount();
273
- ```
274
-
275
- ### 2. Resolve test profile by GTIN
212
+ ## Public Methods
276
213
 
277
- ```ts
278
- const profile = await safeCdx.getTestProfileByGTIN("850024942325");
214
+ ### `getTestProfilesByAccount(...)`
279
215
 
280
- const userTestResultId = profile.Data.ID;
281
- const gtin = profile.Data.DiagnosticProfile.TestInfo.gtin;
282
- ```
216
+ Lists test profiles available to the authenticated patient account. The backend resolves the tenant from the authenticated patient context.
283
217
 
284
- ### 3. Create upload URL
218
+ #### Signature
285
219
 
286
220
  ```ts
287
- const upload = await safeCdx.createUploadUrl(userTestResultId, gtin);
288
-
289
- const preSignedURL = upload.Data.preSignedURL;
290
- const imageOfCaptureId = upload.Data.Metadata.UploadId;
221
+ safeCdx.getTestProfilesByAccount(
222
+ includeRegisterTestDetails?: boolean
223
+ ): Promise<APIResponse<GetTestProfilesByAccountData>>
291
224
  ```
292
225
 
293
- ### 4. Upload image
226
+ `GetTestProfilesByAccountData` is:
294
227
 
295
228
  ```ts
296
- await safeCdx.uploadImage(preSignedURL, imageBody, "image/jpeg");
229
+ type GetTestProfilesByAccountData =
230
+ SafeAPIResponse<TestProfileByAccountItem[]>;
297
231
  ```
298
232
 
299
- ### 5. Update CVML status
233
+ #### Parameters
300
234
 
301
- ```ts
302
- await safeCdx.updateCvmlStatus(imageOfCaptureId, "ImageProcessing");
303
- ```
235
+ | Parameter | Type | Required | Description |
236
+ |---|---|---:|---|
237
+ | `includeRegisterTestDetails` | `boolean` | No | Includes registration-specific test details when `true`. Defaults to `true`. |
304
238
 
305
- ### 6. Poll CVML results
239
+ #### Usage
306
240
 
307
241
  ```ts
308
- const cvmlResults = await safeCdx.getCvmlResults(imageOfCaptureId);
242
+ const profiles = await safeCdx.getTestProfilesByAccount();
309
243
  ```
310
244
 
311
- ### 7. Submit answers
312
-
313
245
  ```ts
314
- await safeCdx.submitAnswers({
315
- UserTestResultId: userTestResultId,
316
- Result: [
317
- {
318
- Analyte: "Purchase",
319
- ReportedValue: "drug store",
320
- StoredValue: "drug store",
321
- Score: "0",
322
- },
323
- ],
324
- });
246
+ const profiles = await safeCdx.getTestProfilesByAccount(true);
325
247
  ```
326
248
 
327
- ### 8. Finalize test
249
+ #### API request sent internally
328
250
 
329
- ```ts
330
- await safeCdx.finalizeTest({
331
- UserTestResultId: userTestResultId,
332
- });
251
+ ```json
252
+ {
253
+ "Data": {
254
+ "IncludeRegisterTestDetails": true
255
+ }
256
+ }
333
257
  ```
334
258
 
335
- ### 9. Retrieve result data
259
+ #### API response example
336
260
 
337
- ```ts
338
- const details = await safeCdx.getResultDetails(userTestResultId);
339
-
340
- const pdf = await safeCdx.getResultPdf({
341
- UserTestResultId: userTestResultId,
342
- });
343
-
344
- const imageCaptureUrl = await safeCdx.getImageCaptureUrl(userTestResultId);
261
+ ```json
262
+ {
263
+ "Data": {
264
+ "success": true,
265
+ "data": [
266
+ {
267
+ "gtin": "810172700093",
268
+ "productName": "Speedy Swab COVID-19 + Flu Self-Test",
269
+ "description": "",
270
+ "testKitImageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/4307c803740644ab8c4594f69783c1d8-638694157517533356.png",
271
+ "language": "en",
272
+ "registerTestDetail": {
273
+ "title": "Scan the barcode",
274
+ "description": "<p><span style=\"font-size: 12pt; font-family: Avenir, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;\">Hold your phone over the barcode on the box.</span></p>",
275
+ "buttonTitle": "Scan Code",
276
+ "imageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/1ccc293f924a4c5bb2c5fc76f474c625-638769599179275771.png"
277
+ }
278
+ },
279
+ {
280
+ "gtin": "860002060439",
281
+ "productName": "Winx UTI Test + Treat",
282
+ "description": "",
283
+ "testKitImageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/36649cb6377c435a9e4f83af0f2fea46-639058689126409862.png",
284
+ "language": "en",
285
+ "registerTestDetail": {
286
+ "title": "",
287
+ "description": "<p></p>",
288
+ "buttonTitle": "Scan Barcode on the Box",
289
+ "imageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/ad72c9b8c3324c1cb7aa9dbf29e46d64-638905990491115782.png"
290
+ }
291
+ },
292
+ {
293
+ "gtin": "850024942325",
294
+ "productName": "Vaginal Health pH Test + Treat",
295
+ "description": "",
296
+ "testKitImageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/3cebedbb69cb4455af33da5ab524ec93-639058688331122684.png",
297
+ "language": "en",
298
+ "registerTestDetail": {
299
+ "title": "",
300
+ "description": "<p></p>",
301
+ "buttonTitle": "Scan Barcode on the Box",
302
+ "imageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/1475ae0004084d749ee2dfc9123af952-638905989776168388.png"
303
+ }
304
+ }
305
+ ],
306
+ "message": "Success",
307
+ "code": 0
308
+ },
309
+ "ErrorMessage": null,
310
+ "IsOK": true
311
+ }
345
312
  ```
346
313
 
347
- ## State Management
314
+ ---
315
+
316
+ ### `getTestProfileByGTIN(...)`
348
317
 
349
- The connector does not manage the full Safe CDX flow state.
318
+ Resolves the Safe CDX test profile associated with a GTIN barcode.
350
319
 
351
- The application should keep track of values returned between steps, such as:
320
+ #### Signature
352
321
 
353
322
  ```ts
354
- const state = {
355
- gtin: undefined,
356
- userTestResultId: undefined,
357
- preSignedURL: undefined,
358
- imageOfCaptureId: undefined,
359
- };
323
+ safeCdx.getTestProfileByGTIN(gtin: string): Promise<APIResponse<GetTestProfileByGTINData>>
360
324
  ```
361
325
 
362
- Important values:
326
+ #### Parameters
363
327
 
364
- - `gtin`: product or test kit identifier
365
- - `userTestResultId`: test result record ID used across the flow
366
- - `preSignedURL`: temporary URL used for image upload
367
- - `imageOfCaptureId`: upload identifier returned after creating the upload URL
328
+ | Parameter | Type | Required | Description |
329
+ |---|---|---:|---|
330
+ | `gtin` | `string` | Yes | GTIN barcode value used to resolve the test profile. |
368
331
 
369
- These values should be stored by the consuming app and passed to the next SDK method when needed.
370
-
371
- ## Public Methods
372
-
373
- ### getTestProfileByGTIN
332
+ #### Usage
374
333
 
375
334
  ```ts
376
- const profile = await safeCdx.getTestProfileByGTIN(gtin);
335
+ const profile = await safeCdx.getTestProfileByGTIN("850024942325");
377
336
  ```
378
337
 
379
- Resolves a test profile by GTIN barcode.
380
338
 
381
- #### API Response example
339
+ #### API response example
382
340
 
383
341
  ```json
384
342
  {
@@ -568,92 +526,55 @@ Resolves a test profile by GTIN barcode.
568
526
  }
569
527
  ```
570
528
 
571
- ### getTestProfilesByAccount
529
+ ---
572
530
 
573
- ```ts
574
- const profiles = await safeCdx.getTestProfilesByAccount();
575
- ```
531
+ ### `createUploadUrl(...)`
576
532
 
577
- Lists test profiles available to the authenticated account.
533
+ Creates a pre-signed URL for uploading a test image.
578
534
 
579
- A custom payload may also be provided:
535
+ #### Signature
580
536
 
581
537
  ```ts
582
- const profiles = await safeCdx.getTestProfilesByAccount({
583
- IncludeRegisterTestDetails: true,
584
- });
538
+ safeCdx.createUploadUrl(
539
+ userTestResultId: string,
540
+ gtin: string,
541
+ imageType?: string
542
+ ): Promise<APIResponse<CreateUploadUrlData>>
585
543
  ```
586
544
 
587
- #### API Response example
545
+ #### Parameters
588
546
 
589
- ```json
590
- {
591
- "Data": {
592
- "success": true,
593
- "data": [
594
- {
595
- "gtin": "810172700093",
596
- "productName": "Speedy Swab COVID-19 + Flu Self-Test",
597
- "description": "",
598
- "testKitImageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/4307c803740644ab8c4594f69783c1d8-638694157517533356.png",
599
- "language": "en",
600
- "registerTestDetail": {
601
- "title": "Scan the barcode",
602
- "description": "<p><span style=\"font-size: 12pt; font-family: Avenir, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;\">Hold your phone over the barcode on the box.</span></p>",
603
- "buttonTitle": "Scan Code",
604
- "imageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/1ccc293f924a4c5bb2c5fc76f474c625-638769599179275771.png"
605
- }
606
- },
607
- {
608
- "gtin": "860002060439",
609
- "productName": "Winx UTI Test + Treat",
610
- "description": "",
611
- "testKitImageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/36649cb6377c435a9e4f83af0f2fea46-639058689126409862.png",
612
- "language": "en",
613
- "registerTestDetail": {
614
- "title": "",
615
- "description": "<p></p>",
616
- "buttonTitle": "Scan Barcode on the Box",
617
- "imageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/ad72c9b8c3324c1cb7aa9dbf29e46d64-638905990491115782.png"
618
- }
619
- },
620
- {
621
- "gtin": "850024942325",
622
- "productName": "Vaginal Health pH Test + Treat",
623
- "description": "",
624
- "testKitImageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/3cebedbb69cb4455af33da5ab524ec93-639058688331122684.png",
625
- "language": "en",
626
- "registerTestDetail": {
627
- "title": "",
628
- "description": "<p></p>",
629
- "buttonTitle": "Scan Barcode on the Box",
630
- "imageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/1475ae0004084d749ee2dfc9123af952-638905989776168388.png"
631
- }
632
- }
633
- ],
634
- "message": "Success",
635
- "code": 0
636
- },
637
- "ErrorMessage": null,
638
- "IsOK": true
639
- }
640
- ```
547
+ | Parameter | Type | Required | Description |
548
+ |---|---|---:|---|
549
+ | `userTestResultId` | `string` | Yes | Test result identifier reused through the workflow. |
550
+ | `gtin` | `string` | Yes | GTIN associated with the selected test. |
551
+ | `imageType` | `string` | No | Image type used in upload metadata. Defaults to `"jpg"`. |
641
552
 
642
- ### createUploadUrl
553
+ #### Usage
643
554
 
644
555
  ```ts
645
556
  const upload = await safeCdx.createUploadUrl(
646
- userTestResultId,
647
- gtin,
648
- "jpg"
557
+ userTestResultId,
558
+ gtin,
559
+ "jpg"
649
560
  );
650
561
  ```
651
562
 
652
- Creates a pre-signed image upload URL.
563
+ #### API request sent internally
653
564
 
654
- `imageType` is optional. If not provided, the client sends `jpg`.
565
+ ```json
566
+ {
567
+ "Data": {
568
+ "Gtin": "<GTIN>",
569
+ "UserTestResultId": "<USER_TEST_RESULT_ID>",
570
+ "Metadata": {
571
+ "ImageType": "jpg"
572
+ }
573
+ }
574
+ }
575
+ ```
655
576
 
656
- #### API Response example
577
+ #### API response example
657
578
 
658
579
  ```json
659
580
  {
@@ -670,30 +591,92 @@ Creates a pre-signed image upload URL.
670
591
  }
671
592
  ```
672
593
 
673
- ### uploadImage
594
+ ---
595
+
596
+ ### `uploadImage(...)`
597
+
598
+ Uploads an image directly to the pre-signed URL returned by `createUploadUrl(...)`. This method does not call a Safe CDX API route and does not send Health Cloud authentication headers.
599
+
600
+ #### Signature
674
601
 
675
602
  ```ts
676
- await safeCdx.uploadImage(preSignedURL, imageBody, "image/jpeg");
603
+ safeCdx.uploadImage(
604
+ preSignedURL: string,
605
+ image: BodyInit,
606
+ contentType?: string
607
+ ): Promise<void>
677
608
  ```
678
609
 
679
- Uploads the image directly to the pre-signed URL.
610
+ #### Parameters
680
611
 
681
- This method does not call a Safe CDX API route.
612
+ | Parameter | Type | Required | Description |
613
+ |---|---|---:|---|
614
+ | `preSignedURL` | `string` | Yes | Temporary storage upload URL returned by `createUploadUrl(...)`. |
615
+ | `image` | `BodyInit` | Yes | Raw image body, such as a `File`, `Blob` or compatible upload body. |
616
+ | `contentType` | `string` | No | MIME type of the uploaded file. Defaults to `"image/jpeg"`. |
682
617
 
683
- ### updateCvmlStatus
618
+ #### Usage
684
619
 
685
620
  ```ts
686
- const result = await safeCdx.updateCvmlStatus(
687
- imageOfCaptureId,
688
- "ImageProcessing"
621
+ await safeCdx.uploadImage(
622
+ preSignedURL,
623
+ imageBody,
624
+ "image/jpeg"
689
625
  );
690
626
  ```
691
627
 
692
- Updates the CVML processing status for a captured image.
628
+ A failed direct upload throws `SafeCDXError`.
629
+
630
+ ---
693
631
 
694
- The client returns the received response directly without applying `ApiResponse` validation.
632
+ ### `updateCvmlStatus(...)`
695
633
 
696
- #### API Response example
634
+ Updates the CVML processing status for a captured image. This endpoint returns `SafeAPIResponse<T>` directly.
635
+
636
+ #### Signature
637
+
638
+ ```ts
639
+ safeCdx.updateCvmlStatus(
640
+ imageOfCaptureId: string,
641
+ cvmlStatus: string
642
+ ): Promise<UpdateCvmlStatusResponse>
643
+ ```
644
+
645
+ `UpdateCvmlStatusResponse` is:
646
+
647
+ ```ts
648
+ type UpdateCvmlStatusResponse =
649
+ SafeAPIResponse<EntityReferenceData>;
650
+ ```
651
+
652
+ #### Parameters
653
+
654
+ | Parameter | Type | Required | Description |
655
+ |---|---|---:|---|
656
+ | `imageOfCaptureId` | `string` | Yes | Identifier returned in the image upload metadata. |
657
+ | `cvmlStatus` | `string` | Yes | CVML processing status to apply, such as `"ImageProcessing"`. |
658
+
659
+ #### Usage
660
+
661
+ ```ts
662
+ const response = await safeCdx.updateCvmlStatus(
663
+ imageOfCaptureId,
664
+ "ImageProcessing"
665
+ );
666
+ ```
667
+
668
+ #### API request sent internally
669
+
670
+ ```json
671
+ {
672
+ "Data": {
673
+ "ImageOfCaptureId": "<IMAGE_OF_CAPTURE_ID>",
674
+ "CvmlStatus": "ImageProcessing"
675
+ }
676
+ }
677
+ ```
678
+
679
+ #### API response example
697
680
 
698
681
  ```json
699
682
  {
@@ -706,17 +689,40 @@ The client returns the received response directly without applying `ApiResponse`
706
689
  }
707
690
  ```
708
691
 
709
- ### getCvmlResults
692
+ ---
693
+
694
+ ### `getCvmlResults(...)`
695
+
696
+ Polls CVML results for a captured image. This endpoint returns `SafeAPIResponse<T>` directly.
697
+
698
+ #### Signature
699
+
700
+ ```ts
701
+ safeCdx.getCvmlResults(
702
+ imageOfCaptureId: string
703
+ ): Promise<GetCvmlResultsResponse>
704
+ ```
705
+
706
+ `GetCvmlResultsResponse` is:
710
707
 
711
708
  ```ts
712
- const result = await safeCdx.getCvmlResults(imageOfCaptureId);
709
+ type GetCvmlResultsResponse =
710
+ SafeAPIResponse<CvmlResultsData>;
713
711
  ```
714
712
 
715
- Polls CVML analysis results for a captured image.
713
+ #### Parameters
716
714
 
717
- The client returns the received response directly without applying `ApiResponse` validation.
715
+ | Parameter | Type | Required | Description |
716
+ |---|---|---:|---|
717
+ | `imageOfCaptureId` | `string` | Yes | Identifier of the captured image being processed. |
718
718
 
719
- #### API Response example
719
+ #### Usage
720
+
721
+ ```ts
722
+ const response = await safeCdx.getCvmlResults(imageOfCaptureId);
723
+ ```
724
+
725
+ #### API response example
720
726
 
721
727
  ```json
722
728
  {
@@ -760,33 +766,56 @@ The client returns the received response directly without applying `ApiResponse`
760
766
  }
761
767
  ```
762
768
 
763
- ### getPendingResults
769
+ ---
770
+
771
+ ### `getPendingResults(...)`
772
+
773
+ Returns pending or incomplete test results for the authenticated patient.
774
+
775
+ #### Signature
764
776
 
765
777
  ```ts
766
- const pending = await safeCdx.getPendingResults();
778
+ safeCdx.getPendingResults(
779
+ excludeStatus?: string
780
+ ): Promise<APIResponse<GetPendingResultsData>>
767
781
  ```
768
782
 
769
- Returns pending or incomplete test results.
770
-
771
- When called without an argument, the SDK sends:
783
+ `GetPendingResultsData` is:
772
784
 
773
785
  ```ts
774
- {
775
- Data: {
776
- ExcludeStatus: "Invalid,Canceled",
777
- },
778
- }
786
+ type GetPendingResultsData =
787
+ SafeAPIResponse<TestResultSummary[]>;
779
788
  ```
780
789
 
781
- The same request can be made explicitly:
790
+ #### Parameters
791
+
792
+ | Parameter | Type | Required | Description |
793
+ |---|---|---:|---|
794
+ | `excludeStatus` | `string` | No | Comma-separated statuses excluded from the returned results. Defaults to `"Invalid,Canceled"`. |
795
+
796
+ #### Usage
782
797
 
783
798
  ```ts
784
- const pending = await safeCdx.getPendingResults({
785
- ExcludeStatus: "Invalid,Canceled",
786
- });
799
+ const pending = await safeCdx.getPendingResults();
787
800
  ```
788
801
 
789
- #### API Response example
802
+ ```ts
803
+ const pending = await safeCdx.getPendingResults(
804
+ "Invalid,Canceled"
805
+ );
806
+ ```
807
+
808
+ #### API request sent internally
809
+
810
+ ```json
811
+ {
812
+ "Data": {
813
+ "ExcludeStatus": "Invalid,Canceled"
814
+ }
815
+ }
816
+ ```
817
+
818
+ #### API response example
790
819
 
791
820
  ```json
792
821
  {
@@ -866,25 +895,54 @@ const pending = await safeCdx.getPendingResults({
866
895
  }
867
896
  ```
868
897
 
869
- ### getLastResults
898
+ ---
899
+
900
+ ### `getLastResults(...)`
901
+
902
+ Returns the most recent test result for the authenticated patient.
903
+
904
+ #### Signature
905
+
906
+ ```ts
907
+ safeCdx.getLastResults(
908
+ excludeStatus?: string
909
+ ): Promise<APIResponse<GetLastResultsData>>
910
+ ```
911
+
912
+ `GetLastResultsData` is:
870
913
 
871
914
  ```ts
872
- const last = await safeCdx.getLastResults();
915
+ type GetLastResultsData =
916
+ SafeAPIResponse<TestResultDetails>;
873
917
  ```
874
918
 
875
- Returns the most recent test result for the authenticated patient.
919
+ #### Parameters
920
+
921
+ | Parameter | Type | Required | Description |
922
+ |---|---|---:|---|
923
+ | `excludeStatus` | `string` | No | Status excluded from the returned result. Defaults to `"Invalid"`. |
876
924
 
877
- When called without an argument, the SDK sends:
925
+ #### Usage
878
926
 
879
927
  ```ts
928
+ const lastResult = await safeCdx.getLastResults();
929
+ ```
930
+
931
+ ```ts
932
+ const lastResult = await safeCdx.getLastResults("Invalid");
933
+ ```
934
+
935
+ #### API request sent internally
936
+
937
+ ```json
880
938
  {
881
- Data: {
882
- ExcludeStatus: "Invalid",
883
- },
939
+ "Data": {
940
+ "ExcludeStatus": "Invalid"
941
+ }
884
942
  }
885
943
  ```
886
944
 
887
- #### API Response example
945
+ #### API response example
888
946
 
889
947
  ```json
890
948
  {
@@ -1028,25 +1086,54 @@ When called without an argument, the SDK sends:
1028
1086
  }
1029
1087
  ```
1030
1088
 
1031
- ### getTestHistory
1089
+ ---
1090
+
1091
+ ### `getTestHistory(...)`
1092
+
1093
+ Returns test result history for the authenticated patient.
1094
+
1095
+ #### Signature
1032
1096
 
1033
1097
  ```ts
1034
- const history = await safeCdx.getTestHistory();
1098
+ safeCdx.getTestHistory(
1099
+ excludeStatus?: string
1100
+ ): Promise<APIResponse<GetTestHistoryData>>
1101
+ ```
1102
+
1103
+ `GetTestHistoryData` is:
1104
+
1105
+ ```ts
1106
+ type GetTestHistoryData =
1107
+ SafeAPIResponse<TestResultSummary[]>;
1035
1108
  ```
1036
1109
 
1037
- Returns the test history for the authenticated patient.
1110
+ #### Parameters
1111
+
1112
+ | Parameter | Type | Required | Description |
1113
+ |---|---|---:|---|
1114
+ | `excludeStatus` | `string` | No | Status excluded from the returned history. Defaults to `"Invalid"`. |
1038
1115
 
1039
- When called without an argument, the SDK sends:
1116
+ #### Usage
1040
1117
 
1041
1118
  ```ts
1119
+ const history = await safeCdx.getTestHistory();
1120
+ ```
1121
+
1122
+ ```ts
1123
+ const history = await safeCdx.getTestHistory("Invalid");
1124
+ ```
1125
+
1126
+ #### API request sent internally
1127
+
1128
+ ```json
1042
1129
  {
1043
- Data: {
1044
- ExcludeStatus: "Invalid",
1045
- },
1130
+ "Data": {
1131
+ "ExcludeStatus": "Invalid"
1132
+ }
1046
1133
  }
1047
1134
  ```
1048
1135
 
1049
- #### API Response example
1136
+ #### API response example
1050
1137
 
1051
1138
  ```json
1052
1139
  {
@@ -1087,17 +1174,50 @@ When called without an argument, the SDK sends:
1087
1174
  }
1088
1175
  ```
1089
1176
 
1090
- ### getResultDetails
1177
+ ---
1178
+
1179
+ ### `getResultDetails(...)`
1180
+
1181
+ Returns detailed data for a specific test attempt. This endpoint returns `SafeAPIResponse<T>` directly.
1182
+
1183
+ #### Signature
1184
+
1185
+ ```ts
1186
+ safeCdx.getResultDetails(
1187
+ userTestResultId: string
1188
+ ): Promise<GetResultDetailsResponse>
1189
+ ```
1190
+
1191
+ `GetResultDetailsResponse` is:
1192
+
1193
+ ```ts
1194
+ type GetResultDetailsResponse =
1195
+ SafeAPIResponse<TestResultDetails>;
1196
+ ```
1197
+
1198
+ #### Parameters
1199
+
1200
+ | Parameter | Type | Required | Description |
1201
+ |---|---|---:|---|
1202
+ | `userTestResultId` | `string` | Yes | Identifier of the test attempt. |
1203
+
1204
+ #### Usage
1091
1205
 
1092
1206
  ```ts
1093
1207
  const details = await safeCdx.getResultDetails(userTestResultId);
1094
1208
  ```
1095
1209
 
1096
- Returns detailed result data for a specific test attempt.
1210
+ #### API request sent internally
1097
1211
 
1098
- The client returns the received response directly without applying `ApiResponse` validation.
1212
+ ```json
1213
+ {
1214
+ "Data": {
1215
+ "UserTestResultId": "<USER_TEST_RESULT_ID>"
1216
+ }
1217
+ }
1218
+ ```
1099
1219
 
1100
- #### API Response example
1220
+ #### API response example
1101
1221
 
1102
1222
  ```json
1103
1223
  {
@@ -1231,17 +1351,47 @@ The client returns the received response directly without applying `ApiResponse`
1231
1351
  }
1232
1352
  ```
1233
1353
 
1234
- ### getResultPdf
1354
+ ---
1355
+
1356
+ ### `getResultPdf(...)`
1357
+
1358
+ Requests the PDF result for a specific test attempt.
1359
+
1360
+ #### Signature
1235
1361
 
1236
1362
  ```ts
1237
- const pdf = await safeCdx.getResultPdf({
1238
- UserTestResultId: userTestResultId,
1239
- });
1363
+ safeCdx.getResultPdf(userTestResultId: string): Promise<APIResponse<GetResultPdfData>>
1240
1364
  ```
1241
1365
 
1242
- Requests the PDF result for a specific test result.
1366
+ `GetResultPdfData` is:
1367
+
1368
+ ```ts
1369
+ type GetResultPdfData = SafeAPIResponse<string>;
1370
+ ```
1243
1371
 
1244
- #### API Response example
1372
+ #### Parameters
1373
+
1374
+ | Parameter | Type | Required | Description |
1375
+ |---|---|---:|---|
1376
+ | `userTestResultId` | `string` | Yes | Identifier of the test attempt. |
1377
+
1378
+ #### Usage
1379
+
1380
+ ```ts
1381
+ const pdf = await safeCdx.getResultPdf(userTestResultId);
1382
+ ```
1383
+
1384
+ #### API request sent internally
1385
+
1386
+ ```json
1387
+ {
1388
+ "Data": {
1389
+ "UserTestResultId": "<USER_TEST_RESULT_ID>"
1390
+ }
1391
+ }
1392
+ ```
1393
+
1394
+ #### API response example
1245
1395
 
1246
1396
  ```json
1247
1397
  {
@@ -1256,17 +1406,50 @@ Requests the PDF result for a specific test result.
1256
1406
  }
1257
1407
  ```
1258
1408
 
1259
- ### getImageCaptureUrl
1409
+ ---
1410
+
1411
+ ### `getImageCaptureUrl(...)`
1412
+
1413
+ Requests image capture data for a specific test attempt. This endpoint returns `SafeAPIResponse<T>` directly.
1414
+
1415
+ #### Signature
1416
+
1417
+ ```ts
1418
+ safeCdx.getImageCaptureUrl(
1419
+ userTestResultId: string
1420
+ ): Promise<GetImageCaptureUrlResponse>
1421
+ ```
1422
+
1423
+ `GetImageCaptureUrlResponse` is:
1424
+
1425
+ ```ts
1426
+ type GetImageCaptureUrlResponse =
1427
+ SafeAPIResponse<string>;
1428
+ ```
1429
+
1430
+ #### Parameters
1431
+
1432
+ | Parameter | Type | Required | Description |
1433
+ |---|---|---:|---|
1434
+ | `userTestResultId` | `string` | Yes | Identifier of the test attempt. |
1435
+
1436
+ #### Usage
1260
1437
 
1261
1438
  ```ts
1262
1439
  const imageUrl = await safeCdx.getImageCaptureUrl(userTestResultId);
1263
1440
  ```
1264
1441
 
1265
- Requests image capture data for a specific test result.
1442
+ #### API request sent internally
1266
1443
 
1267
- The client returns the received response directly without applying `ApiResponse` validation.
1444
+ ```json
1445
+ {
1446
+ "Data": {
1447
+ "UserTestResultId": "<USER_TEST_RESULT_ID>"
1448
+ }
1449
+ }
1450
+ ```
1268
1451
 
1269
- #### API Response example
1452
+ #### API response example
1270
1453
 
1271
1454
  ```json
1272
1455
  {
@@ -1277,21 +1460,60 @@ The client returns the received response directly without applying `ApiResponse`
1277
1460
  }
1278
1461
  ```
1279
1462
 
1280
- ### resumeFlow
1463
+ ---
1464
+
1465
+ ### `resumeFlow(...)`
1466
+
1467
+ Marks a test attempt as resumed or not resumed.
1468
+
1469
+ #### Signature
1281
1470
 
1282
1471
  ```ts
1283
- const resumed = await safeCdx.resumeFlow(userTestResultId);
1472
+ safeCdx.resumeFlow(
1473
+ userTestResultId: string,
1474
+ resumed?: boolean
1475
+ ): Promise<APIResponse<ResumeFlowData>>
1284
1476
  ```
1285
1477
 
1286
- Marks a test attempt as resumed.
1478
+ `ResumeFlowData` is:
1479
+
1480
+ ```ts
1481
+ type ResumeFlowData =
1482
+ SafeAPIResponse<ResumeFlowResult>;
1483
+ ```
1287
1484
 
1288
- The second argument can be used to explicitly set the resumed value:
1485
+ #### Parameters
1486
+
1487
+ | Parameter | Type | Required | Description |
1488
+ |---|---|---:|---|
1489
+ | `userTestResultId` | `string` | Yes | Identifier of the test attempt. |
1490
+ | `resumed` | `boolean` | No | Resume state sent to the API. Defaults to `true`. |
1491
+
1492
+ #### Usage
1289
1493
 
1290
1494
  ```ts
1291
- const resumed = await safeCdx.resumeFlow(userTestResultId, true);
1495
+ const response = await safeCdx.resumeFlow(userTestResultId);
1292
1496
  ```
1293
1497
 
1294
- #### API Response example
1498
+ ```ts
1499
+ const response = await safeCdx.resumeFlow(
1500
+ userTestResultId,
1501
+ true
1502
+ );
1503
+ ```
1504
+
1505
+ #### API request sent internally
1506
+
1507
+ ```json
1508
+ {
1509
+ "Data": {
1510
+ "UserTestResultId": "<USER_TEST_RESULT_ID>",
1511
+ "Resumed": true
1512
+ }
1513
+ }
1514
+ ```
1515
+
1516
+ #### API response example
1295
1517
 
1296
1518
  ```json
1297
1519
  {
@@ -1313,17 +1535,69 @@ const resumed = await safeCdx.resumeFlow(userTestResultId, true);
1313
1535
  }
1314
1536
  ```
1315
1537
 
1316
- ### submitAnswers
1538
+ ---
1539
+
1540
+ ### `submitAnswers(...)`
1541
+
1542
+ Submits analyte answers for a test attempt. This method accepts a structured object because the request contains a collection of answer entries.
1543
+
1544
+ #### Signature
1545
+
1546
+ ```ts
1547
+ safeCdx.submitAnswers(
1548
+ submission: SubmitAnswersRequest
1549
+ ): Promise<APIResponse<SubmitAnswersData>>
1550
+ ```
1551
+
1552
+ `SubmitAnswersData` is:
1553
+
1554
+ ```ts
1555
+ type SubmitAnswersData =
1556
+ SafeAPIResponse<EntityReferenceData>;
1557
+ ```
1558
+
1559
+ #### Parameters
1560
+
1561
+ | Parameter | Type | Required | Description |
1562
+ |---|---|---:|---|
1563
+ | `submission.UserTestResultId` | `string` | Yes | Identifier of the test attempt. |
1564
+ | `submission.Result` | `AnswerResult[]` | Yes | Answer entries submitted for the test attempt. |
1565
+
1566
+ #### Usage
1317
1567
 
1318
1568
  ```ts
1319
- const result = await safeCdx.submitAnswers(payload);
1569
+ const response = await safeCdx.submitAnswers({
1570
+ UserTestResultId: userTestResultId,
1571
+ Result: [
1572
+ {
1573
+ Analyte: "Purchase",
1574
+ ReportedValue: "drug store",
1575
+ StoredValue: "drug store",
1576
+ Score: "0"
1577
+ }
1578
+ ]
1579
+ });
1320
1580
  ```
1321
1581
 
1322
- Submits analyte answers for a test attempt.
1582
+ #### API request sent internally
1323
1583
 
1324
- `Result` should contain at least one answer item. The connector does not generate analyte answers automatically.
1584
+ ```json
1585
+ {
1586
+ "Data": {
1587
+ "UserTestResultId": "<USER_TEST_RESULT_ID>",
1588
+ "Result": [
1589
+ {
1590
+ "Analyte": "Purchase",
1591
+ "ReportedValue": "drug store",
1592
+ "StoredValue": "drug store",
1593
+ "Score": "0"
1594
+ }
1595
+ ]
1596
+ }
1597
+ }
1598
+ ```
1325
1599
 
1326
- #### API Response example
1600
+ #### API response example
1327
1601
 
1328
1602
  ```json
1329
1603
  {
@@ -1340,36 +1614,48 @@ Submits analyte answers for a test attempt.
1340
1614
  }
1341
1615
  ```
1342
1616
 
1343
- ### finalizeTest
1617
+ ---
1618
+
1619
+ ### `finalizeTest(...)`
1620
+
1621
+ Finalizes the test attempt identified by `userTestResultId`.
1622
+
1623
+ #### Signature
1344
1624
 
1345
1625
  ```ts
1346
- const result = await safeCdx.finalizeTest({
1347
- UserTestResultId: userTestResultId,
1348
- });
1626
+ safeCdx.finalizeTest(userTestResultId: string): Promise<APIResponse<FinalizeTestData>>
1349
1627
  ```
1350
1628
 
1351
- Finalizes a test by `UserTestResultId`.
1629
+ #### Parameters
1630
+
1631
+ | Parameter | Type | Required | Description |
1632
+ |---|---|---:|---|
1633
+ | `userTestResultId` | `string` | Yes | Identifier of the test attempt to finalize. |
1352
1634
 
1353
- The SDK sends:
1635
+ #### Usage
1354
1636
 
1355
1637
  ```ts
1638
+ const response = await safeCdx.finalizeTest(userTestResultId);
1639
+ ```
1640
+
1641
+ #### API request sent internally
1642
+
1643
+ ```json
1356
1644
  {
1357
- Data: {
1358
- UserTestResultId: userTestResultId,
1359
- },
1645
+ "Data": {
1646
+ "UserTestResultId": "<USER_TEST_RESULT_ID>"
1647
+ }
1360
1648
  }
1361
1649
  ```
1362
1650
 
1363
1651
 
1364
1652
  ## Notes
1365
1653
 
1366
- - The connector does not perform login.
1367
- - The connector uses `HCLoginClient` for authenticated request headers.
1654
+ - `HCSafeCDXClient` documents the authenticated patient workflow supported by the current client.
1655
+ - The connector does not perform login; it uses `HCLoginClient` for authenticated patient headers.
1656
+ - Safe CDX uses its own service URL derived from `loginClient.getEnvironment()`.
1368
1657
  - The connector does not use `loginClient.getBaseUrl()` for Safe CDX API routes.
1369
- - Safe CDX derives its service base URL from `loginClient.getEnvironment()`.
1370
- - Standard Safe CDX API methods use the shared `HttpClient`.
1371
1658
  - POST payloads are wrapped internally as `{ Data: payload }`.
1372
- - The caller should pass only the inner payload to SDK methods.
1373
- - Image upload uses the pre-signed URL directly and sends the raw file body.
1374
- - Authorization headers should not be sent to the pre-signed upload URL.
1375
- - Pre-signed URLs are time-bound and should not be logged in production.
1659
+ - Patient tenant and identity context are supplied by the backend from the authenticated request.
1660
+ - API responses are returned in their endpoint-defined shape without being unwrapped or transformed by `HCSafeCDXClient`.
1661
+ - Direct image upload uses the pre-signed URL and sends the raw file body without Health Cloud authorization headers.