@healthcloudai/hc-safe-cdx 0.4.1 → 0.4.2
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 +340 -1211
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# Healthcheck Safe CDX Connector
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
SDK client for the Safe CDX diagnostic test workflow. `HCSafeCDXClient` handles authenticated requests to the Safe CDX service on behalf of a logged-in patient.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Image upload is handled separately because the upload target is a pre-signed storage URL rather than a Safe CDX API route.
|
|
5
|
+
---
|
|
8
6
|
|
|
9
7
|
## Installation
|
|
10
8
|
|
|
@@ -12,17 +10,19 @@ Image upload is handled separately because the upload target is a pre-signed sto
|
|
|
12
10
|
npm install @healthcloudai/hc-safe-cdx @healthcloudai/hc-http @healthcloudai/hc-login-connector
|
|
13
11
|
```
|
|
14
12
|
|
|
13
|
+
---
|
|
14
|
+
|
|
15
15
|
## Import
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
|
-
import { FetchClient } from "@healthcloudai/hc-http";
|
|
19
|
-
import { HCLoginClient } from "@healthcloudai/hc-login-connector";
|
|
20
18
|
import { HCSafeCDXClient } from "@healthcloudai/hc-safe-cdx";
|
|
19
|
+
import { HCLoginClient } from "@healthcloudai/hc-login-connector";
|
|
20
|
+
import { FetchClient } from "@healthcloudai/hc-http";
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
---
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
## Setup
|
|
26
26
|
|
|
27
27
|
```ts
|
|
28
28
|
const httpClient = new FetchClient();
|
|
@@ -34,485 +34,207 @@ await loginClient.login(email, password);
|
|
|
34
34
|
const safeCdx = new HCSafeCDXClient(httpClient, loginClient);
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
## Client Configuration
|
|
40
|
-
|
|
41
|
-
### Service URL
|
|
42
|
-
|
|
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.
|
|
37
|
+
Safe CDX derives its own service URL from `loginClient.getEnvironment()`. It does **not** use `loginClient.getBaseUrl()`.
|
|
44
38
|
|
|
39
|
+
---
|
|
45
40
|
|
|
46
|
-
|
|
41
|
+
## API Key
|
|
47
42
|
|
|
48
|
-
When
|
|
43
|
+
When the environment requires an API key:
|
|
49
44
|
|
|
50
45
|
```ts
|
|
51
46
|
safeCdx.setApiKey("x-api-key", apiKeyValue);
|
|
52
47
|
```
|
|
53
48
|
|
|
54
|
-
|
|
49
|
+
---
|
|
55
50
|
|
|
56
|
-
##
|
|
51
|
+
## Service URL
|
|
57
52
|
|
|
58
|
-
|
|
53
|
+
Safe CDX calls a different backend from all other connectors.
|
|
59
54
|
|
|
60
|
-
|
|
55
|
+
| Environment | Base URL |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `dev` | `https://dev-api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
|
|
58
|
+
| `uat` | `https://uat-api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
|
|
59
|
+
| `prod` | `https://api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
interface APIRequest<T> {
|
|
64
|
-
Data: T;
|
|
65
|
-
}
|
|
66
|
-
```
|
|
61
|
+
---
|
|
67
62
|
|
|
68
|
-
|
|
63
|
+
## Response Shapes
|
|
69
64
|
|
|
70
|
-
|
|
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
|
-
});
|
|
82
|
-
```
|
|
65
|
+
Safe CDX methods return one of three distinct shapes. Read them differently depending on the method — check the table at the bottom.
|
|
83
66
|
|
|
84
|
-
|
|
67
|
+
### Shape A — `APIResponse<T>` (standard Health Cloud envelope)
|
|
85
68
|
|
|
86
|
-
```
|
|
69
|
+
```ts
|
|
87
70
|
{
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
{
|
|
92
|
-
"Analyte": "Purchase",
|
|
93
|
-
"ReportedValue": "drug store",
|
|
94
|
-
"StoredValue": "drug store",
|
|
95
|
-
"Score": "0"
|
|
96
|
-
}
|
|
97
|
-
]
|
|
98
|
-
}
|
|
71
|
+
IsOK: boolean;
|
|
72
|
+
ErrorMessage: string | null;
|
|
73
|
+
Data: T | null; // actual payload directly in Data
|
|
99
74
|
}
|
|
100
75
|
```
|
|
101
76
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Safe CDX methods return the response shape produced by their backend endpoint without unwrapping or transforming it.
|
|
105
|
-
|
|
106
|
-
Most documented methods return the standard Health Cloud response envelope:
|
|
107
|
-
|
|
77
|
+
Access:
|
|
108
78
|
```ts
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
ErrorMessage: string | null;
|
|
79
|
+
const r = await safeCdx.getTestProfileByGTIN(gtin);
|
|
80
|
+
if (r.IsOK) {
|
|
81
|
+
const id = r.Data?.ID;
|
|
113
82
|
}
|
|
114
83
|
```
|
|
115
84
|
|
|
116
|
-
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### Shape B — `APIResponse<SafeAPIResponse<T>>` (double-wrapped)
|
|
88
|
+
|
|
89
|
+
The Health Cloud gateway wraps the SafeCDX vendor response without normalising it. `Data` is itself a vendor envelope.
|
|
117
90
|
|
|
118
91
|
```ts
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
92
|
+
{
|
|
93
|
+
IsOK: boolean;
|
|
94
|
+
ErrorMessage: string | null;
|
|
95
|
+
Data: {
|
|
96
|
+
success: boolean; // vendor-level success flag
|
|
97
|
+
data: T | null; // actual payload
|
|
122
98
|
message: string | null;
|
|
123
99
|
code: number;
|
|
100
|
+
} | null;
|
|
124
101
|
}
|
|
125
102
|
```
|
|
126
103
|
|
|
127
|
-
|
|
128
|
-
|
|
104
|
+
Access — always check **both** levels:
|
|
129
105
|
```ts
|
|
130
|
-
safeCdx.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
106
|
+
const r = await safeCdx.listTestProfilesByAccount();
|
|
107
|
+
if (r.IsOK && r.Data?.success) {
|
|
108
|
+
const profiles = r.Data.data; // T is here
|
|
109
|
+
}
|
|
110
|
+
// r.IsOK can be true while r.Data?.success is false
|
|
111
|
+
// r.Data?.message contains the vendor error description
|
|
134
112
|
```
|
|
135
113
|
|
|
136
|
-
|
|
114
|
+
---
|
|
137
115
|
|
|
138
|
-
|
|
116
|
+
### Shape C — `SafeAPIResponse<T>` (vendor envelope only, no outer wrapper)
|
|
139
117
|
|
|
140
|
-
|
|
118
|
+
Four methods return the vendor structure directly — no `APIResponse` outer layer.
|
|
141
119
|
|
|
142
120
|
```ts
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
safeCdx.getLastResults(excludeStatus?)
|
|
150
|
-
safeCdx.listTestHistory(excludeStatus?)
|
|
151
|
-
safeCdx.getResultDetails(userTestResultId)
|
|
152
|
-
safeCdx.getResultPdf(userTestResultId)
|
|
153
|
-
safeCdx.getImageCaptureUrl(userTestResultId)
|
|
154
|
-
safeCdx.resumeFlow(userTestResultId, resumed?)
|
|
155
|
-
safeCdx.finalizeTest(userTestResultId)
|
|
121
|
+
{
|
|
122
|
+
success: boolean;
|
|
123
|
+
data: T | null;
|
|
124
|
+
message: string | null;
|
|
125
|
+
code: number;
|
|
126
|
+
}
|
|
156
127
|
```
|
|
157
128
|
|
|
129
|
+
Access:
|
|
130
|
+
```ts
|
|
131
|
+
const r = await safeCdx.getCvmlResults(imageOfCaptureId);
|
|
132
|
+
if (r.success) {
|
|
133
|
+
const cvmlStatus = r.data?.cvmlStatus;
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
158
138
|
|
|
159
139
|
## Typical Patient Workflow
|
|
160
140
|
|
|
161
141
|
```ts
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (!profile.IsOK || !profile.Data) {
|
|
167
|
-
throw new Error(profile.ErrorMessage ?? "Unable to resolve the test profile.");
|
|
142
|
+
// 1. List available test profiles
|
|
143
|
+
const profilesResponse = await safeCdx.listTestProfilesByAccount();
|
|
144
|
+
if (!profilesResponse.IsOK || !profilesResponse.Data?.success) {
|
|
145
|
+
throw new Error(profilesResponse.Data?.message ?? profilesResponse.ErrorMessage ?? "Failed to list profiles");
|
|
168
146
|
}
|
|
147
|
+
const profiles = profilesResponse.Data.data; // TestProfileByAccountItem[]
|
|
169
148
|
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
throw new Error("The selected test profile does not include the required workflow identifiers.");
|
|
149
|
+
// 2. Resolve profile by GTIN
|
|
150
|
+
const profileResponse = await safeCdx.getTestProfileByGTIN(profiles[0].gtin);
|
|
151
|
+
if (!profileResponse.IsOK || !profileResponse.Data) {
|
|
152
|
+
throw new Error(profileResponse.ErrorMessage ?? "Failed to resolve profile");
|
|
175
153
|
}
|
|
154
|
+
const userTestResultId = profileResponse.Data.ID!;
|
|
155
|
+
const gtin = profileResponse.Data.DiagnosticProfile?.TestInfo.gtin ?? profiles[0].gtin;
|
|
176
156
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (!
|
|
180
|
-
|
|
157
|
+
// 3. Create upload URL
|
|
158
|
+
const uploadResponse = await safeCdx.createUploadUrl(userTestResultId, gtin);
|
|
159
|
+
if (!uploadResponse.IsOK || !uploadResponse.Data?.preSignedURL) {
|
|
160
|
+
throw new Error(uploadResponse.ErrorMessage ?? "Failed to get upload URL");
|
|
181
161
|
}
|
|
162
|
+
const preSignedURL = uploadResponse.Data.preSignedURL;
|
|
163
|
+
const imageOfCaptureId = uploadResponse.Data.Metadata!.UploadId!;
|
|
182
164
|
|
|
183
|
-
|
|
184
|
-
const imageOfCaptureId = upload.Data.Metadata.UploadId;
|
|
185
|
-
|
|
165
|
+
// 4. Upload image (direct to S3 — no auth headers)
|
|
186
166
|
await safeCdx.uploadImage(preSignedURL, imageBody, "image/jpeg");
|
|
167
|
+
|
|
168
|
+
// 5. Notify CVML processing started
|
|
187
169
|
await safeCdx.updateCvmlStatus(imageOfCaptureId, "ImageProcessing");
|
|
188
170
|
|
|
189
|
-
|
|
171
|
+
// 6. Get CVML analysis results
|
|
172
|
+
const cvml = await safeCdx.getCvmlResults(imageOfCaptureId);
|
|
173
|
+
if (!cvml.success) throw new Error(cvml.message ?? "CVML failed");
|
|
190
174
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
Analyte: "Purchase",
|
|
196
|
-
ReportedValue: "drug store",
|
|
197
|
-
StoredValue: "drug store",
|
|
198
|
-
Score: "0"
|
|
199
|
-
}
|
|
200
|
-
]
|
|
175
|
+
// 7. Submit analyte answers
|
|
176
|
+
const submitResponse = await safeCdx.submitAnswers({
|
|
177
|
+
UserTestResultId: userTestResultId,
|
|
178
|
+
Result: [{ Analyte: "Purchase", ReportedValue: "drug store", StoredValue: "drug store", Score: "0" }],
|
|
201
179
|
});
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const details = await safeCdx.getResultDetails(userTestResultId);
|
|
206
|
-
const pdf = await safeCdx.getResultPdf(userTestResultId);
|
|
207
|
-
const imageCaptureUrl = await safeCdx.getImageCaptureUrl(userTestResultId);
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
The application is responsible for preserving workflow values returned between steps, including `userTestResultId`, `gtin`, `preSignedURL`, and `imageOfCaptureId`.
|
|
211
|
-
|
|
212
|
-
## Public Methods
|
|
213
|
-
|
|
214
|
-
### `listTestProfilesByAccount(...)`
|
|
215
|
-
|
|
216
|
-
Lists test profiles available to the authenticated patient account. The backend resolves the tenant from the authenticated patient context.
|
|
217
|
-
|
|
218
|
-
#### Signature
|
|
219
|
-
|
|
220
|
-
```ts
|
|
221
|
-
safeCdx.listTestProfilesByAccount(
|
|
222
|
-
includeRegisterTestDetails?: boolean
|
|
223
|
-
): Promise<SafeAPIResponse<TestProfileByAccountItem[]>>
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
#### Parameters
|
|
227
|
-
|
|
228
|
-
| Parameter | Type | Required | Description |
|
|
229
|
-
|---|---|---:|---|
|
|
230
|
-
| `includeRegisterTestDetails` | `boolean` | No | Includes registration-specific test details when `true`. Defaults to `true`. |
|
|
231
|
-
|
|
232
|
-
#### Usage
|
|
233
|
-
|
|
234
|
-
```ts
|
|
235
|
-
const profiles = await safeCdx.listTestProfilesByAccount();
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
```ts
|
|
239
|
-
const profiles = await safeCdx.listTestProfilesByAccount(true);
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
#### API request sent internally
|
|
243
|
-
|
|
244
|
-
```json
|
|
245
|
-
{
|
|
246
|
-
"Data": {
|
|
247
|
-
"IncludeRegisterTestDetails": true
|
|
248
|
-
}
|
|
180
|
+
if (!submitResponse.IsOK || !submitResponse.Data?.success) {
|
|
181
|
+
throw new Error(submitResponse.Data?.message ?? submitResponse.ErrorMessage ?? "Submit failed");
|
|
249
182
|
}
|
|
250
|
-
```
|
|
251
183
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
```json
|
|
255
|
-
{
|
|
256
|
-
"Data": {
|
|
257
|
-
"success": true,
|
|
258
|
-
"data": [
|
|
259
|
-
{
|
|
260
|
-
"gtin": "810172700093",
|
|
261
|
-
"productName": "Speedy Swab COVID-19 + Flu Self-Test",
|
|
262
|
-
"description": "",
|
|
263
|
-
"testKitImageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/4307c803740644ab8c4594f69783c1d8-638694157517533356.png",
|
|
264
|
-
"language": "en",
|
|
265
|
-
"registerTestDetail": {
|
|
266
|
-
"title": "Scan the barcode",
|
|
267
|
-
"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>",
|
|
268
|
-
"buttonTitle": "Scan Code",
|
|
269
|
-
"imageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/1ccc293f924a4c5bb2c5fc76f474c625-638769599179275771.png"
|
|
270
|
-
}
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
"gtin": "860002060439",
|
|
274
|
-
"productName": "Winx UTI Test + Treat",
|
|
275
|
-
"description": "",
|
|
276
|
-
"testKitImageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/36649cb6377c435a9e4f83af0f2fea46-639058689126409862.png",
|
|
277
|
-
"language": "en",
|
|
278
|
-
"registerTestDetail": {
|
|
279
|
-
"title": "",
|
|
280
|
-
"description": "<p></p>",
|
|
281
|
-
"buttonTitle": "Scan Barcode on the Box",
|
|
282
|
-
"imageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/ad72c9b8c3324c1cb7aa9dbf29e46d64-638905990491115782.png"
|
|
283
|
-
}
|
|
284
|
-
},
|
|
285
|
-
{
|
|
286
|
-
"gtin": "850024942325",
|
|
287
|
-
"productName": "Vaginal Health pH Test + Treat",
|
|
288
|
-
"description": "",
|
|
289
|
-
"testKitImageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/3cebedbb69cb4455af33da5ab524ec93-639058688331122684.png",
|
|
290
|
-
"language": "en",
|
|
291
|
-
"registerTestDetail": {
|
|
292
|
-
"title": "",
|
|
293
|
-
"description": "<p></p>",
|
|
294
|
-
"buttonTitle": "Scan Barcode on the Box",
|
|
295
|
-
"imageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/RegisterTest/Image/1475ae0004084d749ee2dfc9123af952-638905989776168388.png"
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
],
|
|
299
|
-
"message": "Success",
|
|
300
|
-
"code": 0
|
|
301
|
-
},
|
|
302
|
-
"ErrorMessage": null,
|
|
303
|
-
"IsOK": true
|
|
304
|
-
}
|
|
184
|
+
// 8. Finalize
|
|
185
|
+
await safeCdx.finalizeTest(userTestResultId);
|
|
305
186
|
```
|
|
306
187
|
|
|
307
188
|
---
|
|
308
189
|
|
|
309
|
-
|
|
190
|
+
## Methods
|
|
310
191
|
|
|
311
|
-
|
|
192
|
+
### `getTestProfileByGTIN(gtin)` — GET
|
|
312
193
|
|
|
313
|
-
|
|
194
|
+
Resolves the test profile for a GTIN barcode. Returns **Shape A**.
|
|
314
195
|
|
|
315
196
|
```ts
|
|
316
197
|
safeCdx.getTestProfileByGTIN(gtin: string): Promise<APIResponse<GetTestProfileByGTINData>>
|
|
317
198
|
```
|
|
318
199
|
|
|
319
|
-
#### Parameters
|
|
320
|
-
|
|
321
|
-
| Parameter | Type | Required | Description |
|
|
322
|
-
|---|---|---:|---|
|
|
323
|
-
| `gtin` | `string` | Yes | GTIN barcode value used to resolve the test profile. |
|
|
324
|
-
|
|
325
|
-
#### Usage
|
|
326
|
-
|
|
327
200
|
```ts
|
|
328
|
-
const
|
|
201
|
+
const r = await safeCdx.getTestProfileByGTIN("850024942325");
|
|
202
|
+
// r.IsOK, r.Data?.ID, r.Data?.DiagnosticProfile
|
|
329
203
|
```
|
|
330
204
|
|
|
331
|
-
|
|
332
|
-
#### API response example
|
|
205
|
+
Example response:
|
|
333
206
|
|
|
334
207
|
```json
|
|
335
208
|
{
|
|
336
209
|
"Data": {
|
|
337
210
|
"ID": "6a104bba6068dd9a213db810",
|
|
338
|
-
"FHIRID": null,
|
|
339
|
-
"CQLID": null,
|
|
340
211
|
"TestName": "UTI Test + Treat",
|
|
341
212
|
"CustomTestName": "UTI Test + Treat",
|
|
342
213
|
"IsDeactivated": false,
|
|
343
214
|
"IsOrderable": true,
|
|
344
|
-
"EnablePublicHealthReporting": true,
|
|
345
|
-
"IsSelfAssessmentMode": true,
|
|
346
215
|
"CaptureMethod": 0,
|
|
347
|
-
"IsMLDisabled": false,
|
|
348
|
-
"ShortName": "UTI Test + Treat",
|
|
349
|
-
"DescriptionHTML": null,
|
|
350
|
-
"OrderableName": "Winx Health",
|
|
351
|
-
"VendorName": null,
|
|
352
|
-
"ManufactureName": null,
|
|
353
|
-
"VendorTestID": "0123",
|
|
354
|
-
"Sku": "SH00131",
|
|
355
|
-
"LabTestType": null,
|
|
356
|
-
"TestClassification": "NOT_SPECIFIED",
|
|
357
|
-
"KitRegistrationRequired": false,
|
|
358
|
-
"RequiresProviderVerificationOfResults": false,
|
|
359
216
|
"DiagnosticProfile": {
|
|
360
217
|
"_id": "6780031a991814469e88237f",
|
|
361
|
-
"Created": "2025-01-09T17:10:50.8Z",
|
|
362
|
-
"Modified": "2026-05-06T12:39:08.732Z",
|
|
363
218
|
"TestInfo": {
|
|
364
219
|
"gtin": "860002060439",
|
|
365
|
-
"testInfoHeader": "Winx Health",
|
|
366
220
|
"productName": "Winx UTI Test + Treat",
|
|
367
|
-
"testCategory": "Rapid",
|
|
368
|
-
"fdaStatus": "EUA",
|
|
369
221
|
"cvmlTestName": "winx-uti",
|
|
370
222
|
"cvmlDuration": 20,
|
|
371
223
|
"isCVMLResult": true,
|
|
372
|
-
"
|
|
373
|
-
"winxhealth",
|
|
374
|
-
"safehealth",
|
|
375
|
-
"test-account",
|
|
376
|
-
"speedyswab",
|
|
377
|
-
"healthcheck"
|
|
378
|
-
],
|
|
379
|
-
"cvmlRetryLimit": 2,
|
|
380
|
-
"failoverEnabled": true,
|
|
381
|
-
"cvmlPollingPolicy": "FullResult"
|
|
224
|
+
"cvmlRetryLimit": 2
|
|
382
225
|
},
|
|
383
226
|
"Analyte": [
|
|
384
227
|
{
|
|
385
228
|
"_id": "1",
|
|
386
|
-
"DisplayOrder": 1,
|
|
387
|
-
"Display": "CVML",
|
|
388
229
|
"Name": "Leukocyte",
|
|
389
230
|
"Question": "What is the Leukocyte reading?",
|
|
390
|
-
"Image": "/analyte/WinxLeuk.png",
|
|
391
231
|
"Responses": [
|
|
392
|
-
{
|
|
393
|
-
|
|
394
|
-
"displayOrder": 1,
|
|
395
|
-
"storedValue": "Neg",
|
|
396
|
-
"cvmlValue": "Neg",
|
|
397
|
-
"score": "0"
|
|
398
|
-
},
|
|
399
|
-
{
|
|
400
|
-
"value": "Positive 70+",
|
|
401
|
-
"displayOrder": 3,
|
|
402
|
-
"storedValue": "70+",
|
|
403
|
-
"cvmlValue": "70+",
|
|
404
|
-
"score": "4"
|
|
405
|
-
}
|
|
406
|
-
]
|
|
407
|
-
},
|
|
408
|
-
{
|
|
409
|
-
"_id": "2",
|
|
410
|
-
"DisplayOrder": 2,
|
|
411
|
-
"Display": "CVML",
|
|
412
|
-
"Name": "Nitrite",
|
|
413
|
-
"Question": "What is the Nitrite reading?",
|
|
414
|
-
"Image": "/analyte/WinxNitr.png",
|
|
415
|
-
"Responses": [
|
|
416
|
-
{
|
|
417
|
-
"value": "Negative",
|
|
418
|
-
"displayOrder": 1,
|
|
419
|
-
"storedValue": "Neg",
|
|
420
|
-
"cvmlValue": "Neg",
|
|
421
|
-
"score": "0"
|
|
422
|
-
},
|
|
423
|
-
{
|
|
424
|
-
"value": "Positive High++",
|
|
425
|
-
"displayOrder": 3,
|
|
426
|
-
"storedValue": "High++",
|
|
427
|
-
"cvmlValue": "High++",
|
|
428
|
-
"score": "2"
|
|
429
|
-
}
|
|
430
|
-
]
|
|
431
|
-
}
|
|
432
|
-
],
|
|
433
|
-
"Cta": [
|
|
434
|
-
{
|
|
435
|
-
"id": "1",
|
|
436
|
-
"title": "No UTI is detected",
|
|
437
|
-
"instructions": "If you're still having symptoms, we recommend talking to your doctor about additional testing.",
|
|
438
|
-
"linkType": "url",
|
|
439
|
-
"linkText": "Find a Doctor",
|
|
440
|
-
"linkValue": "https://book.zocdoc.com/?utm_source=winx&utm_medium=app&utm_campaign=testandtreat"
|
|
441
|
-
}
|
|
442
|
-
],
|
|
443
|
-
"Indication": [
|
|
444
|
-
{
|
|
445
|
-
"id": "4",
|
|
446
|
-
"title": "Indication",
|
|
447
|
-
"text": "Results suggest you have a UTI",
|
|
448
|
-
"recommendTitle": "Recommendation",
|
|
449
|
-
"recommendText": "Drink lots of water and connect with a doctor to discuss treatment and next steps.",
|
|
450
|
-
"ctaId": "2"
|
|
451
|
-
}
|
|
452
|
-
],
|
|
453
|
-
"Decision": [
|
|
454
|
-
{
|
|
455
|
-
"calculation": "Total: analyte.responses.score",
|
|
456
|
-
"results": [
|
|
457
|
-
{
|
|
458
|
-
"indicationId": "4",
|
|
459
|
-
"value": "4"
|
|
460
|
-
}
|
|
232
|
+
{ "value": "Negative -", "storedValue": "Neg", "score": "0" },
|
|
233
|
+
{ "value": "Positive 70+", "storedValue": "70+", "score": "4" }
|
|
461
234
|
]
|
|
462
235
|
}
|
|
463
236
|
]
|
|
464
|
-
}
|
|
465
|
-
"ProductAssetDetail": {
|
|
466
|
-
"imageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/ProductAsset/Image/47197f0c75844ba3b11335f7bfe1959b-639062362985442065.png",
|
|
467
|
-
"body": "<p></p>"
|
|
468
|
-
},
|
|
469
|
-
"DisclaimerDetail": {
|
|
470
|
-
"body": "<p><span style=\"font-family: Avenir, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif; font-size: 14pt;\">At this time please refer back to your paper Instructions For Use (IFU).</span></p>",
|
|
471
|
-
"title": "Record Results",
|
|
472
|
-
"continueButtonTitle": "Continue",
|
|
473
|
-
"showDisclaimer": false
|
|
474
|
-
},
|
|
475
|
-
"ScanImageDetail": {
|
|
476
|
-
"ImageUrl": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/ScanImage/Image/590172d7548e4b3eb09bfe42679fc6b1-638900206144635626.png"
|
|
477
|
-
},
|
|
478
|
-
"Instructions": [
|
|
479
|
-
{
|
|
480
|
-
"NavigationTitle": "Instructional Video",
|
|
481
|
-
"Type": "CollectionInstructionOnly",
|
|
482
|
-
"Title": "Instructional Video",
|
|
483
|
-
"ButtonTitle": "Continue",
|
|
484
|
-
"TimeInSeconds": 0,
|
|
485
|
-
"VideoUrl": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/LabTestOrderable/CollectionInstruction/Video/3a74d7091d65455ab675f8ddd5f9d9d5-638918232791589391.mp4",
|
|
486
|
-
"SequenceOrder": 0
|
|
487
|
-
}
|
|
488
|
-
],
|
|
489
|
-
"BillingInfo": {
|
|
490
|
-
"Taxable": false
|
|
491
|
-
},
|
|
492
|
-
"Associations": null,
|
|
493
|
-
"TestReportAsset": null,
|
|
494
|
-
"Articles": [],
|
|
495
|
-
"TestResults": [
|
|
496
|
-
{
|
|
497
|
-
"PatientTestResult": 0,
|
|
498
|
-
"ResultTitle": "Positive",
|
|
499
|
-
"ResultDisplay": "Positive",
|
|
500
|
-
"TreatmentPlanDescription": "",
|
|
501
|
-
"IsReturnToDashboardAllowed": false,
|
|
502
|
-
"IsOrderTestAllowed": false,
|
|
503
|
-
"IsFindTestRetailerAllowed": false
|
|
504
|
-
}
|
|
505
|
-
],
|
|
506
|
-
"GS1GTINs": null,
|
|
507
|
-
"Included": null,
|
|
508
|
-
"LOINCCodes": null,
|
|
509
|
-
"TestLOINCCodes": null,
|
|
510
|
-
"TestSNOMEDCTCodes": null,
|
|
511
|
-
"Created": "2024-12-23T18:00:48.619Z",
|
|
512
|
-
"Modified": "2026-05-13T17:07:07.048Z",
|
|
513
|
-
"CreatedByID": null,
|
|
514
|
-
"ModifiedByID": null,
|
|
515
|
-
"TenantID": null
|
|
237
|
+
}
|
|
516
238
|
},
|
|
517
239
|
"ErrorMessage": null,
|
|
518
240
|
"IsOK": true
|
|
@@ -521,58 +243,85 @@ const profile = await safeCdx.getTestProfileByGTIN("850024942325");
|
|
|
521
243
|
|
|
522
244
|
---
|
|
523
245
|
|
|
524
|
-
### `
|
|
246
|
+
### `listTestProfilesByAccount(includeRegisterTestDetails?)` — POST
|
|
525
247
|
|
|
526
|
-
|
|
248
|
+
Lists test profiles for the patient's account. Returns **Shape B** — read `r.Data.data` for the array.
|
|
527
249
|
|
|
528
|
-
|
|
250
|
+
| Parameter | Type | Default | Description |
|
|
251
|
+
|---|---|---|---|
|
|
252
|
+
| `includeRegisterTestDetails` | `boolean` | `true` | Include registration UI details |
|
|
529
253
|
|
|
530
254
|
```ts
|
|
531
|
-
safeCdx.
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
imageType?: string
|
|
535
|
-
): Promise<APIResponse<CreateUploadUrlData>>
|
|
255
|
+
safeCdx.listTestProfilesByAccount(
|
|
256
|
+
includeRegisterTestDetails?: boolean
|
|
257
|
+
): Promise<APIResponse<SafeAPIResponse<TestProfileByAccountItem[]>>>
|
|
536
258
|
```
|
|
537
259
|
|
|
538
|
-
#### Parameters
|
|
539
|
-
|
|
540
|
-
| Parameter | Type | Required | Description |
|
|
541
|
-
|---|---|---:|---|
|
|
542
|
-
| `userTestResultId` | `string` | Yes | Test result identifier reused through the workflow. |
|
|
543
|
-
| `gtin` | `string` | Yes | GTIN associated with the selected test. |
|
|
544
|
-
| `imageType` | `string` | No | Image type used in upload metadata. Defaults to `"jpg"`. |
|
|
545
|
-
|
|
546
|
-
#### Usage
|
|
547
|
-
|
|
548
260
|
```ts
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
);
|
|
261
|
+
const r = await safeCdx.listTestProfilesByAccount();
|
|
262
|
+
if (r.IsOK && r.Data?.success) {
|
|
263
|
+
const profiles = r.Data.data; // TestProfileByAccountItem[]
|
|
264
|
+
}
|
|
554
265
|
```
|
|
555
266
|
|
|
556
|
-
|
|
267
|
+
Example response:
|
|
557
268
|
|
|
558
269
|
```json
|
|
559
270
|
{
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
271
|
+
"Data": {
|
|
272
|
+
"success": true,
|
|
273
|
+
"data": [
|
|
274
|
+
{
|
|
275
|
+
"gtin": "860002060439",
|
|
276
|
+
"productName": "Winx UTI Test + Treat",
|
|
277
|
+
"testKitImageURL": "https://safe-content-cache-us-west-2-quality-speed.s3-us-west-2.amazonaws.com/...",
|
|
278
|
+
"language": "en",
|
|
279
|
+
"registerTestDetail": {
|
|
280
|
+
"title": "",
|
|
281
|
+
"buttonTitle": "Scan Barcode on the Box"
|
|
565
282
|
}
|
|
566
|
-
|
|
283
|
+
}
|
|
284
|
+
],
|
|
285
|
+
"message": "Success",
|
|
286
|
+
"code": 0
|
|
287
|
+
},
|
|
288
|
+
"ErrorMessage": null,
|
|
289
|
+
"IsOK": true
|
|
567
290
|
}
|
|
568
291
|
```
|
|
569
292
|
|
|
570
|
-
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
### `createUploadUrl(userTestResultId, gtin, imageType?)` — POST
|
|
296
|
+
|
|
297
|
+
Creates a pre-signed S3 URL for uploading the test image. Returns **Shape A**.
|
|
298
|
+
|
|
299
|
+
| Parameter | Type | Default | Description |
|
|
300
|
+
|---|---|---|---|
|
|
301
|
+
| `userTestResultId` | `string` | — | From `getTestProfileByGTIN` response (`Data.ID`) |
|
|
302
|
+
| `gtin` | `string` | — | GTIN of the selected test |
|
|
303
|
+
| `imageType` | `string` | `"jpg"` | Image format for upload metadata |
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
safeCdx.createUploadUrl(
|
|
307
|
+
userTestResultId: string,
|
|
308
|
+
gtin: string,
|
|
309
|
+
imageType?: string
|
|
310
|
+
): Promise<APIResponse<CreateUploadUrlData>>
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
const r = await safeCdx.createUploadUrl(userTestResultId, gtin);
|
|
315
|
+
const preSignedURL = r.Data?.preSignedURL;
|
|
316
|
+
const imageOfCaptureId = r.Data?.Metadata?.UploadId;
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Example response:
|
|
571
320
|
|
|
572
321
|
```json
|
|
573
322
|
{
|
|
574
323
|
"Data": {
|
|
575
|
-
"preSignedURL": "https://sf-us-west-2-lower-neural-cdx-img-uploads-quality.s3.us-west-2.amazonaws.com/healthcheck/winx-uti
|
|
324
|
+
"preSignedURL": "https://sf-us-west-2-lower-neural-cdx-img-uploads-quality.s3.us-west-2.amazonaws.com/healthcheck/winx-uti/...?X-Amz-Expires=1200&...",
|
|
576
325
|
"Metadata": {
|
|
577
326
|
"UploadId": "860002060439_6a104bba6068dd9a213db810-1",
|
|
578
327
|
"Type": "Rapid Test Kit Upload",
|
|
@@ -586,97 +335,53 @@ const upload = await safeCdx.createUploadUrl(
|
|
|
586
335
|
|
|
587
336
|
---
|
|
588
337
|
|
|
589
|
-
### `uploadImage(
|
|
338
|
+
### `uploadImage(preSignedURL, image, contentType?)` — PUT (direct S3)
|
|
590
339
|
|
|
591
|
-
Uploads
|
|
340
|
+
Uploads the image directly to the pre-signed storage URL. Returns `Promise<void>`.
|
|
592
341
|
|
|
593
|
-
|
|
342
|
+
**Does not call a Safe CDX API route. Does not send Health Cloud auth headers.** The pre-signed URL provides its own credential. Invalid `preSignedURL`, `image`, or `contentType` input throws `ValidationError`. A non-2xx storage response throws an HTTP-status-mapped connector error.
|
|
594
343
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
): Promise<void>
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
#### Parameters
|
|
604
|
-
|
|
605
|
-
| Parameter | Type | Required | Description |
|
|
606
|
-
|---|---|---:|---|
|
|
607
|
-
| `preSignedURL` | `string` | Yes | Temporary storage upload URL returned by `createUploadUrl(...)`. |
|
|
608
|
-
| `image` | `BodyInit` | Yes | Raw image body, such as a `File`, `Blob` or compatible upload body. |
|
|
609
|
-
| `contentType` | `string` | No | MIME type of the uploaded file. Defaults to `"image/jpeg"`. |
|
|
610
|
-
|
|
611
|
-
#### Usage
|
|
344
|
+
| Parameter | Type | Default | Description |
|
|
345
|
+
|---|---|---|---|
|
|
346
|
+
| `preSignedURL` | `string` | — | From `createUploadUrl` response |
|
|
347
|
+
| `image` | `BodyInit` | — | Raw image — `File`, `Blob`, `Buffer`, etc. |
|
|
348
|
+
| `contentType` | `string` | `"image/jpeg"` | MIME type |
|
|
612
349
|
|
|
613
350
|
```ts
|
|
614
|
-
await safeCdx.uploadImage(
|
|
615
|
-
preSignedURL,
|
|
616
|
-
imageBody,
|
|
617
|
-
"image/jpeg"
|
|
618
|
-
);
|
|
351
|
+
await safeCdx.uploadImage(preSignedURL, imageBody, "image/jpeg");
|
|
619
352
|
```
|
|
620
353
|
|
|
621
|
-
A failed direct upload throws `SafeCDXError`.
|
|
622
|
-
|
|
623
354
|
---
|
|
624
355
|
|
|
625
|
-
### `updateCvmlStatus(
|
|
356
|
+
### `updateCvmlStatus(imageOfCaptureId, cvmlStatus)` — POST
|
|
626
357
|
|
|
627
|
-
|
|
358
|
+
Notifies the CVML service of the processing status for an image capture. Returns **Shape C** — read `r.success` and `r.data` directly.
|
|
628
359
|
|
|
629
|
-
|
|
360
|
+
| Parameter | Type | Description |
|
|
361
|
+
|---|---|---|
|
|
362
|
+
| `imageOfCaptureId` | `string` | `UploadId` from `createUploadUrl` metadata |
|
|
363
|
+
| `cvmlStatus` | `string` | Processing status, e.g. `"ImageProcessing"` |
|
|
630
364
|
|
|
631
365
|
```ts
|
|
632
366
|
safeCdx.updateCvmlStatus(
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
): Promise<UpdateCvmlStatusResponse>
|
|
367
|
+
imageOfCaptureId: string,
|
|
368
|
+
cvmlStatus: string
|
|
369
|
+
): Promise<UpdateCvmlStatusResponse> // = SafeAPIResponse<EntityReferenceData>
|
|
636
370
|
```
|
|
637
371
|
|
|
638
|
-
`UpdateCvmlStatusResponse` is:
|
|
639
|
-
|
|
640
|
-
```ts
|
|
641
|
-
type UpdateCvmlStatusResponse =
|
|
642
|
-
SafeAPIResponse<EntityReferenceData>;
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
#### Parameters
|
|
646
|
-
|
|
647
|
-
| Parameter | Type | Required | Description |
|
|
648
|
-
|---|---|---:|---|
|
|
649
|
-
| `imageOfCaptureId` | `string` | Yes | Identifier returned in the image upload metadata. |
|
|
650
|
-
| `cvmlStatus` | `string` | Yes | CVML processing status to apply, such as `"ImageProcessing"`. |
|
|
651
|
-
|
|
652
|
-
#### Usage
|
|
653
|
-
|
|
654
372
|
```ts
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
);
|
|
659
|
-
```
|
|
660
|
-
|
|
661
|
-
#### API request sent internally
|
|
662
|
-
|
|
663
|
-
```json
|
|
664
|
-
{
|
|
665
|
-
"Data": {
|
|
666
|
-
"ImageOfCaptureId": "<IMAGE_OF_CAPTURE_ID>",
|
|
667
|
-
"CvmlStatus": "ImageProcessing"
|
|
668
|
-
}
|
|
373
|
+
const r = await safeCdx.updateCvmlStatus(imageOfCaptureId, "ImageProcessing");
|
|
374
|
+
if (r.success) {
|
|
375
|
+
const id = r.data?._id;
|
|
669
376
|
}
|
|
670
377
|
```
|
|
671
378
|
|
|
672
|
-
|
|
379
|
+
Example response:
|
|
673
380
|
|
|
674
381
|
```json
|
|
675
382
|
{
|
|
676
383
|
"success": true,
|
|
677
|
-
"data": {
|
|
678
|
-
"_id": "6a104bba6068dd9a213db810"
|
|
679
|
-
},
|
|
384
|
+
"data": { "_id": "6a104bba6068dd9a213db810" },
|
|
680
385
|
"message": "Success",
|
|
681
386
|
"code": 0
|
|
682
387
|
}
|
|
@@ -684,75 +389,47 @@ const response = await safeCdx.updateCvmlStatus(
|
|
|
684
389
|
|
|
685
390
|
---
|
|
686
391
|
|
|
687
|
-
### `getCvmlResults(
|
|
392
|
+
### `getCvmlResults(imageOfCaptureId)` — GET
|
|
688
393
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
#### Signature
|
|
394
|
+
Returns CVML analysis results for a captured image. Returns **Shape C**.
|
|
692
395
|
|
|
693
396
|
```ts
|
|
694
397
|
safeCdx.getCvmlResults(
|
|
695
|
-
|
|
696
|
-
): Promise<GetCvmlResultsResponse>
|
|
398
|
+
imageOfCaptureId: string
|
|
399
|
+
): Promise<GetCvmlResultsResponse> // = SafeAPIResponse<CvmlResultsData>
|
|
697
400
|
```
|
|
698
401
|
|
|
699
|
-
`GetCvmlResultsResponse` is:
|
|
700
|
-
|
|
701
402
|
```ts
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
#### Parameters
|
|
707
|
-
|
|
708
|
-
| Parameter | Type | Required | Description |
|
|
709
|
-
|---|---|---:|---|
|
|
710
|
-
| `imageOfCaptureId` | `string` | Yes | Identifier of the captured image being processed. |
|
|
711
|
-
|
|
712
|
-
#### Usage
|
|
713
|
-
|
|
714
|
-
```ts
|
|
715
|
-
const response = await safeCdx.getCvmlResults(imageOfCaptureId);
|
|
403
|
+
const r = await safeCdx.getCvmlResults(imageOfCaptureId);
|
|
404
|
+
if (r.success) {
|
|
405
|
+
const cvmlStatus = r.data?.cvmlStatus; // e.g. "Retake", "Positive", "Negative"
|
|
406
|
+
}
|
|
716
407
|
```
|
|
717
408
|
|
|
718
|
-
|
|
409
|
+
Example response:
|
|
719
410
|
|
|
720
411
|
```json
|
|
721
412
|
{
|
|
722
413
|
"success": true,
|
|
723
414
|
"data": {
|
|
724
|
-
"
|
|
415
|
+
"_id": "6a104bba6068dd9a213db810",
|
|
725
416
|
"status": "Started",
|
|
726
417
|
"cvmlStatus": "Retake",
|
|
727
418
|
"gtin": "860002060439",
|
|
728
419
|
"isCVMLResult": true,
|
|
729
|
-
"isSelfReportingPostTest": false,
|
|
730
|
-
"schemaVersion": 1,
|
|
731
420
|
"capture": {
|
|
732
421
|
"retryCount": 0,
|
|
733
|
-
"retryLimit":
|
|
422
|
+
"retryLimit": 2,
|
|
734
423
|
"failoverEnabled": true,
|
|
735
424
|
"lastCvmlStatus": "Retake",
|
|
736
425
|
"failoverTriggered": false
|
|
737
426
|
},
|
|
738
|
-
"questionnaireSnapshot": {
|
|
739
|
-
"isValid": []
|
|
740
|
-
},
|
|
741
|
-
"resumed": false,
|
|
742
427
|
"imageOfCaptureResult": {
|
|
743
428
|
"parsed": {
|
|
744
429
|
"outcome": "Retake",
|
|
745
|
-
"responseCode": "Err4"
|
|
746
|
-
"responseMessage": "ml_result_code_error_04_msg",
|
|
747
|
-
"responseTitle": "ml_result_code_error_04_title"
|
|
430
|
+
"responseCode": "Err4"
|
|
748
431
|
}
|
|
749
|
-
}
|
|
750
|
-
"failoverEnabled": true,
|
|
751
|
-
"failoverTriggered": false,
|
|
752
|
-
"resumable": false,
|
|
753
|
-
"showValidity": false,
|
|
754
|
-
"showError": true,
|
|
755
|
-
"_id": "6a104bba6068dd9a213db810"
|
|
432
|
+
}
|
|
756
433
|
},
|
|
757
434
|
"message": "Success",
|
|
758
435
|
"code": 0
|
|
@@ -761,54 +438,28 @@ const response = await safeCdx.getCvmlResults(imageOfCaptureId);
|
|
|
761
438
|
|
|
762
439
|
---
|
|
763
440
|
|
|
764
|
-
### `listPendingResults(
|
|
441
|
+
### `listPendingResults(excludeStatus?)` — POST
|
|
765
442
|
|
|
766
|
-
Returns
|
|
443
|
+
Returns incomplete or pending test results for the patient. Returns **Shape B**.
|
|
767
444
|
|
|
768
|
-
|
|
445
|
+
| Parameter | Type | Default | Description |
|
|
446
|
+
|---|---|---|---|
|
|
447
|
+
| `excludeStatus` | `string` | `"Invalid,Canceled"` | Comma-separated statuses to exclude |
|
|
769
448
|
|
|
770
449
|
```ts
|
|
771
450
|
safeCdx.listPendingResults(
|
|
772
|
-
|
|
773
|
-
): Promise<APIResponse<
|
|
451
|
+
excludeStatus?: string
|
|
452
|
+
): Promise<APIResponse<SafeAPIResponse<TestResultSummary[]>>>
|
|
774
453
|
```
|
|
775
454
|
|
|
776
|
-
`GetPendingResultsData` is:
|
|
777
|
-
|
|
778
455
|
```ts
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
#### Parameters
|
|
784
|
-
|
|
785
|
-
| Parameter | Type | Required | Description |
|
|
786
|
-
|---|---|---:|---|
|
|
787
|
-
| `excludeStatus` | `string` | No | Comma-separated statuses excluded from the returned results. Defaults to `"Invalid,Canceled"`. |
|
|
788
|
-
|
|
789
|
-
#### Usage
|
|
790
|
-
|
|
791
|
-
```ts
|
|
792
|
-
const pending = await safeCdx.listPendingResults();
|
|
793
|
-
```
|
|
794
|
-
|
|
795
|
-
```ts
|
|
796
|
-
const pending = await safeCdx.listPendingResults(
|
|
797
|
-
"Invalid,Canceled"
|
|
798
|
-
);
|
|
799
|
-
```
|
|
800
|
-
|
|
801
|
-
#### API request sent internally
|
|
802
|
-
|
|
803
|
-
```json
|
|
804
|
-
{
|
|
805
|
-
"Data": {
|
|
806
|
-
"ExcludeStatus": "Invalid,Canceled"
|
|
807
|
-
}
|
|
456
|
+
const r = await safeCdx.listPendingResults();
|
|
457
|
+
if (r.IsOK && r.Data?.success) {
|
|
458
|
+
const pending = r.Data.data; // TestResultSummary[]
|
|
808
459
|
}
|
|
809
460
|
```
|
|
810
461
|
|
|
811
|
-
|
|
462
|
+
Example response:
|
|
812
463
|
|
|
813
464
|
```json
|
|
814
465
|
{
|
|
@@ -816,68 +467,14 @@ const pending = await safeCdx.listPendingResults(
|
|
|
816
467
|
"success": true,
|
|
817
468
|
"data": [
|
|
818
469
|
{
|
|
470
|
+
"_id": "6a104bba6068dd9a213db810",
|
|
819
471
|
"status": "Started",
|
|
820
472
|
"cvmlStatus": "Retake",
|
|
821
473
|
"gtin": "860002060439",
|
|
822
474
|
"testName": "Winx UTI Test + Treat",
|
|
823
475
|
"recordedDate": "2026-05-22T12:49:26.047Z",
|
|
824
|
-
"isCVMLBackground": false,
|
|
825
|
-
"isCVMLResult": true,
|
|
826
|
-
"isSelfReportingPostTest": false,
|
|
827
|
-
"isSelfReportingPreTest": false,
|
|
828
|
-
"viewed": false,
|
|
829
|
-
"schemaVersion": 1,
|
|
830
|
-
"capture": {
|
|
831
|
-
"retryCount": 0,
|
|
832
|
-
"retryLimit": 0,
|
|
833
|
-
"failoverEnabled": true,
|
|
834
|
-
"lastCvmlStatus": "Retake",
|
|
835
|
-
"failoverTriggered": false
|
|
836
|
-
},
|
|
837
|
-
"questionnaireSnapshot": {
|
|
838
|
-
"isValid": []
|
|
839
|
-
},
|
|
840
|
-
"resumed": false,
|
|
841
|
-
"failoverEnabled": true,
|
|
842
|
-
"failoverTriggered": false,
|
|
843
476
|
"resumable": true,
|
|
844
|
-
"resumeNext": "SelfReporting"
|
|
845
|
-
"showValidity": false,
|
|
846
|
-
"showError": true,
|
|
847
|
-
"_id": "6a104bba6068dd9a213db810"
|
|
848
|
-
},
|
|
849
|
-
{
|
|
850
|
-
"status": "Started",
|
|
851
|
-
"gtin": "810172700093",
|
|
852
|
-
"testName": "Speedy Swab COVID-19 + Flu Self-Test",
|
|
853
|
-
"recordedDate": "2026-05-22T10:11:10.181Z",
|
|
854
|
-
"isCVMLBackground": true,
|
|
855
|
-
"isCVMLResult": false,
|
|
856
|
-
"isSelfReportingPostTest": true,
|
|
857
|
-
"isSelfReportingPreTest": false,
|
|
858
|
-
"viewed": false,
|
|
859
|
-
"schemaVersion": 1,
|
|
860
|
-
"capture": {
|
|
861
|
-
"retryCount": 0,
|
|
862
|
-
"retryLimit": 0,
|
|
863
|
-
"failoverEnabled": true,
|
|
864
|
-
"failoverTriggered": false
|
|
865
|
-
},
|
|
866
|
-
"questionnaireSnapshot": {
|
|
867
|
-
"isValid": [
|
|
868
|
-
{
|
|
869
|
-
"display": "post-test",
|
|
870
|
-
"question": "Do you see a control line (C)?"
|
|
871
|
-
}
|
|
872
|
-
]
|
|
873
|
-
},
|
|
874
|
-
"resumed": false,
|
|
875
|
-
"failoverEnabled": true,
|
|
876
|
-
"failoverTriggered": false,
|
|
877
|
-
"resumable": false,
|
|
878
|
-
"showValidity": false,
|
|
879
|
-
"showError": true,
|
|
880
|
-
"_id": "6a102bbe6068dd9a213db80f"
|
|
477
|
+
"resumeNext": "SelfReporting"
|
|
881
478
|
}
|
|
882
479
|
],
|
|
883
480
|
"message": "Success",
|
|
@@ -890,186 +487,41 @@ const pending = await safeCdx.listPendingResults(
|
|
|
890
487
|
|
|
891
488
|
---
|
|
892
489
|
|
|
893
|
-
### `getLastResults(
|
|
490
|
+
### `getLastResults(excludeStatus?)` — POST
|
|
894
491
|
|
|
895
|
-
Returns the most recent test result for the
|
|
492
|
+
Returns the most recent test result for the patient. Returns **Shape B**. `Data.data` may be `null` if no results exist.
|
|
896
493
|
|
|
897
|
-
|
|
494
|
+
| Parameter | Type | Default | Description |
|
|
495
|
+
|---|---|---|---|
|
|
496
|
+
| `excludeStatus` | `string` | `"Invalid"` | Status to exclude |
|
|
898
497
|
|
|
899
498
|
```ts
|
|
900
499
|
safeCdx.getLastResults(
|
|
901
|
-
|
|
902
|
-
): Promise<APIResponse<
|
|
903
|
-
```
|
|
904
|
-
|
|
905
|
-
`GetLastResultsData` is:
|
|
906
|
-
|
|
907
|
-
```ts
|
|
908
|
-
type GetLastResultsData =
|
|
909
|
-
SafeAPIResponse<TestResultDetails>;
|
|
910
|
-
```
|
|
911
|
-
|
|
912
|
-
#### Parameters
|
|
913
|
-
|
|
914
|
-
| Parameter | Type | Required | Description |
|
|
915
|
-
|---|---|---:|---|
|
|
916
|
-
| `excludeStatus` | `string` | No | Status excluded from the returned result. Defaults to `"Invalid"`. |
|
|
917
|
-
|
|
918
|
-
#### Usage
|
|
919
|
-
|
|
920
|
-
```ts
|
|
921
|
-
const lastResult = await safeCdx.getLastResults();
|
|
500
|
+
excludeStatus?: string
|
|
501
|
+
): Promise<APIResponse<SafeAPIResponse<TestResultDetails>>>
|
|
922
502
|
```
|
|
923
503
|
|
|
924
504
|
```ts
|
|
925
|
-
const
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
#### API request sent internally
|
|
929
|
-
|
|
930
|
-
```json
|
|
931
|
-
{
|
|
932
|
-
"Data": {
|
|
933
|
-
"ExcludeStatus": "Invalid"
|
|
934
|
-
}
|
|
505
|
+
const r = await safeCdx.getLastResults();
|
|
506
|
+
if (r.IsOK && r.Data?.success && r.Data.data) {
|
|
507
|
+
const latest = r.Data.data; // TestResultDetails
|
|
935
508
|
}
|
|
936
509
|
```
|
|
937
510
|
|
|
938
|
-
|
|
511
|
+
Example response:
|
|
939
512
|
|
|
940
513
|
```json
|
|
941
514
|
{
|
|
942
515
|
"Data": {
|
|
943
516
|
"success": true,
|
|
944
517
|
"data": {
|
|
945
|
-
"
|
|
946
|
-
"userID": "5d00527a-2a9c-4d68-a7f4-8f154b2ca3e6",
|
|
947
|
-
"tenantID": "healthcheck",
|
|
948
|
-
"isSelfAssessment": false,
|
|
518
|
+
"_id": "6a104bba6068dd9a213db810",
|
|
949
519
|
"status": "Started",
|
|
950
520
|
"cvmlStatus": "Retake",
|
|
951
521
|
"gtin": "860002060439",
|
|
952
|
-
"testInfoHeader": "Winx Health",
|
|
953
522
|
"testName": "Winx UTI Test + Treat",
|
|
954
|
-
"imageOfCapture": "860002060439_6a104bba6068dd9a213db810-1.jpg",
|
|
955
|
-
"imageOfCaptureUploadAt": "2026-05-22T12:31:18.333Z",
|
|
956
|
-
"imageOfCaptureResults": [
|
|
957
|
-
{
|
|
958
|
-
"imageOfCapture": "860002060439_6a104bba6068dd9a213db810-1.jpg",
|
|
959
|
-
"uploadAt": "2026-05-22T12:31:18.333Z",
|
|
960
|
-
"cvmlStatus": "Retake",
|
|
961
|
-
"parsed": {
|
|
962
|
-
"outcome": "Retake",
|
|
963
|
-
"responseCode": "Err4",
|
|
964
|
-
"responseMessage": "ml_result_code_error_04_msg",
|
|
965
|
-
"responseTitle": "ml_result_code_error_04_title"
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
],
|
|
969
|
-
"resultPDF": "archive/2026/05/22/12/48/7ecfd730-c4c3-4cd6-8b12-4210b9a2db28.pdf",
|
|
970
|
-
"result": [
|
|
971
|
-
{
|
|
972
|
-
"analyte": "Purchase",
|
|
973
|
-
"reportedValue": "drug store",
|
|
974
|
-
"storedValue": "drug store",
|
|
975
|
-
"score": "0"
|
|
976
|
-
}
|
|
977
|
-
],
|
|
978
523
|
"recordedDate": "2026-05-22T12:49:26.047Z",
|
|
979
|
-
"
|
|
980
|
-
"isCVMLBackground": false,
|
|
981
|
-
"isCVMLResult": true,
|
|
982
|
-
"isSelfReportingPostTest": false,
|
|
983
|
-
"isSelfReportingPreTest": false,
|
|
984
|
-
"viewed": false,
|
|
985
|
-
"finalized": false,
|
|
986
|
-
"schemaVersion": 1,
|
|
987
|
-
"capture": {
|
|
988
|
-
"retryCount": 1,
|
|
989
|
-
"retryLimit": 2,
|
|
990
|
-
"failoverEnabled": true,
|
|
991
|
-
"lastCvmlStatus": "Retake",
|
|
992
|
-
"failoverTriggered": false
|
|
993
|
-
},
|
|
994
|
-
"questionnaireSnapshot": {
|
|
995
|
-
"isValid": [],
|
|
996
|
-
"analyte": [
|
|
997
|
-
{
|
|
998
|
-
"_id": "1",
|
|
999
|
-
"displayOrder": 1,
|
|
1000
|
-
"display": "CVML",
|
|
1001
|
-
"name": "Leukocyte",
|
|
1002
|
-
"question": "What is the Leukocyte reading?",
|
|
1003
|
-
"image": "WinxLeuk.png",
|
|
1004
|
-
"cvmlOrder": 1,
|
|
1005
|
-
"responses": [
|
|
1006
|
-
{
|
|
1007
|
-
"value": "Negative -",
|
|
1008
|
-
"displayOrder": 1,
|
|
1009
|
-
"storedValue": "Neg",
|
|
1010
|
-
"cvmlValue": "Neg",
|
|
1011
|
-
"score": "0"
|
|
1012
|
-
},
|
|
1013
|
-
{
|
|
1014
|
-
"value": "Trace 15+-",
|
|
1015
|
-
"displayOrder": 2,
|
|
1016
|
-
"storedValue": "15+-",
|
|
1017
|
-
"cvmlValue": "15+-",
|
|
1018
|
-
"score": "3"
|
|
1019
|
-
},
|
|
1020
|
-
{
|
|
1021
|
-
"value": "Positive 70+",
|
|
1022
|
-
"displayOrder": 3,
|
|
1023
|
-
"storedValue": "70+",
|
|
1024
|
-
"cvmlValue": "70+",
|
|
1025
|
-
"score": "4"
|
|
1026
|
-
}
|
|
1027
|
-
]
|
|
1028
|
-
},
|
|
1029
|
-
{
|
|
1030
|
-
"_id": "2",
|
|
1031
|
-
"displayOrder": 2,
|
|
1032
|
-
"display": "CVML",
|
|
1033
|
-
"name": "Nitrite",
|
|
1034
|
-
"question": "What is the Nitrite reading?",
|
|
1035
|
-
"image": "WinxNitr.png",
|
|
1036
|
-
"cvmlOrder": 2,
|
|
1037
|
-
"responses": [
|
|
1038
|
-
{
|
|
1039
|
-
"value": "Negative",
|
|
1040
|
-
"displayOrder": 1,
|
|
1041
|
-
"storedValue": "Neg",
|
|
1042
|
-
"cvmlValue": "Neg",
|
|
1043
|
-
"score": "0"
|
|
1044
|
-
},
|
|
1045
|
-
{
|
|
1046
|
-
"value": "Positive Low+",
|
|
1047
|
-
"displayOrder": 2,
|
|
1048
|
-
"storedValue": "Low+",
|
|
1049
|
-
"cvmlValue": "Low+",
|
|
1050
|
-
"score": "2"
|
|
1051
|
-
},
|
|
1052
|
-
{
|
|
1053
|
-
"value": "Positive High++",
|
|
1054
|
-
"displayOrder": 3,
|
|
1055
|
-
"storedValue": "High++",
|
|
1056
|
-
"cvmlValue": "High++",
|
|
1057
|
-
"score": "2"
|
|
1058
|
-
}
|
|
1059
|
-
]
|
|
1060
|
-
}
|
|
1061
|
-
]
|
|
1062
|
-
},
|
|
1063
|
-
"resumed": false,
|
|
1064
|
-
"language": "en",
|
|
1065
|
-
"labVendor": "Winx Health",
|
|
1066
|
-
"labTestType": "Home Test (Rapid)",
|
|
1067
|
-
"externalId": "test-user@example.com",
|
|
1068
|
-
"positiveAnalytes": [],
|
|
1069
|
-
"_id": "6a104bba6068dd9a213db810",
|
|
1070
|
-
"modifier": "5d00527a-2a9c-4d68-a7f4-8f154b2ca3e6",
|
|
1071
|
-
"created": "2026-05-22T12:27:38.731Z",
|
|
1072
|
-
"modified": "2026-05-22T12:49:26.047Z"
|
|
524
|
+
"finalized": false
|
|
1073
525
|
},
|
|
1074
526
|
"message": "Success",
|
|
1075
527
|
"code": 0
|
|
@@ -1081,52 +533,28 @@ const lastResult = await safeCdx.getLastResults("Invalid");
|
|
|
1081
533
|
|
|
1082
534
|
---
|
|
1083
535
|
|
|
1084
|
-
### `listTestHistory(
|
|
536
|
+
### `listTestHistory(excludeStatus?)` — POST
|
|
1085
537
|
|
|
1086
|
-
Returns test result history for the
|
|
538
|
+
Returns the full test result history for the patient. Returns **Shape B**.
|
|
1087
539
|
|
|
1088
|
-
|
|
540
|
+
| Parameter | Type | Default | Description |
|
|
541
|
+
|---|---|---|---|
|
|
542
|
+
| `excludeStatus` | `string` | `"Invalid"` | Status to exclude |
|
|
1089
543
|
|
|
1090
544
|
```ts
|
|
1091
545
|
safeCdx.listTestHistory(
|
|
1092
|
-
|
|
1093
|
-
): Promise<APIResponse<
|
|
546
|
+
excludeStatus?: string
|
|
547
|
+
): Promise<APIResponse<SafeAPIResponse<TestResultSummary[]>>>
|
|
1094
548
|
```
|
|
1095
549
|
|
|
1096
|
-
`GetTestHistoryData` is:
|
|
1097
|
-
|
|
1098
550
|
```ts
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
#### Parameters
|
|
1104
|
-
|
|
1105
|
-
| Parameter | Type | Required | Description |
|
|
1106
|
-
|---|---|---:|---|
|
|
1107
|
-
| `excludeStatus` | `string` | No | Status excluded from the returned history. Defaults to `"Invalid"`. |
|
|
1108
|
-
|
|
1109
|
-
#### Usage
|
|
1110
|
-
|
|
1111
|
-
```ts
|
|
1112
|
-
const history = await safeCdx.listTestHistory();
|
|
1113
|
-
```
|
|
1114
|
-
|
|
1115
|
-
```ts
|
|
1116
|
-
const history = await safeCdx.listTestHistory("Invalid");
|
|
1117
|
-
```
|
|
1118
|
-
|
|
1119
|
-
#### API request sent internally
|
|
1120
|
-
|
|
1121
|
-
```json
|
|
1122
|
-
{
|
|
1123
|
-
"Data": {
|
|
1124
|
-
"ExcludeStatus": "Invalid"
|
|
1125
|
-
}
|
|
551
|
+
const r = await safeCdx.listTestHistory();
|
|
552
|
+
if (r.IsOK && r.Data?.success) {
|
|
553
|
+
const history = r.Data.data; // TestResultSummary[]
|
|
1126
554
|
}
|
|
1127
555
|
```
|
|
1128
556
|
|
|
1129
|
-
|
|
557
|
+
Example response:
|
|
1130
558
|
|
|
1131
559
|
```json
|
|
1132
560
|
{
|
|
@@ -1134,29 +562,11 @@ const history = await safeCdx.listTestHistory("Invalid");
|
|
|
1134
562
|
"success": true,
|
|
1135
563
|
"data": [
|
|
1136
564
|
{
|
|
565
|
+
"_id": "6a104bba6068dd9a213db810",
|
|
1137
566
|
"status": "Started",
|
|
1138
|
-
"cvmlStatus": "Retake",
|
|
1139
|
-
"testInfoHeader": "Winx Health",
|
|
1140
567
|
"testName": "Winx UTI Test + Treat",
|
|
1141
568
|
"recordedDate": "2026-05-22T12:49:26.047Z",
|
|
1142
|
-
"
|
|
1143
|
-
"schemaVersion": 1,
|
|
1144
|
-
"overallResult": "Unknown",
|
|
1145
|
-
"positiveAnalytes": [],
|
|
1146
|
-
"_id": "6a104bba6068dd9a213db810",
|
|
1147
|
-
"created": "2026-05-22T12:27:38.731Z"
|
|
1148
|
-
},
|
|
1149
|
-
{
|
|
1150
|
-
"status": "Started",
|
|
1151
|
-
"testInfoHeader": "Biolabs International",
|
|
1152
|
-
"testName": "Speedy Swab COVID-19 + Flu Self-Test",
|
|
1153
|
-
"recordedDate": "2026-05-22T10:11:10.181Z",
|
|
1154
|
-
"viewed": false,
|
|
1155
|
-
"schemaVersion": 1,
|
|
1156
|
-
"overallResult": "Unknown",
|
|
1157
|
-
"positiveAnalytes": [],
|
|
1158
|
-
"_id": "6a102bbe6068dd9a213db80f",
|
|
1159
|
-
"created": "2026-05-22T10:11:10.181Z"
|
|
569
|
+
"overallResult": "Unknown"
|
|
1160
570
|
}
|
|
1161
571
|
],
|
|
1162
572
|
"message": "Success",
|
|
@@ -1169,175 +579,38 @@ const history = await safeCdx.listTestHistory("Invalid");
|
|
|
1169
579
|
|
|
1170
580
|
---
|
|
1171
581
|
|
|
1172
|
-
### `getResultDetails(
|
|
1173
|
-
|
|
1174
|
-
Returns detailed data for a specific test attempt. This endpoint returns `SafeAPIResponse<T>` directly.
|
|
582
|
+
### `getResultDetails(userTestResultId)` — POST
|
|
1175
583
|
|
|
1176
|
-
|
|
584
|
+
Returns full details for a specific test attempt. Returns **Shape C** — read `r.success` and `r.data` directly.
|
|
1177
585
|
|
|
1178
586
|
```ts
|
|
1179
587
|
safeCdx.getResultDetails(
|
|
1180
|
-
|
|
1181
|
-
): Promise<GetResultDetailsResponse>
|
|
588
|
+
userTestResultId: string
|
|
589
|
+
): Promise<GetResultDetailsResponse> // = SafeAPIResponse<TestResultDetails>
|
|
1182
590
|
```
|
|
1183
591
|
|
|
1184
|
-
`GetResultDetailsResponse` is:
|
|
1185
|
-
|
|
1186
592
|
```ts
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
#### Parameters
|
|
1192
|
-
|
|
1193
|
-
| Parameter | Type | Required | Description |
|
|
1194
|
-
|---|---|---:|---|
|
|
1195
|
-
| `userTestResultId` | `string` | Yes | Identifier of the test attempt. |
|
|
1196
|
-
|
|
1197
|
-
#### Usage
|
|
1198
|
-
|
|
1199
|
-
```ts
|
|
1200
|
-
const details = await safeCdx.getResultDetails(userTestResultId);
|
|
1201
|
-
```
|
|
1202
|
-
|
|
1203
|
-
#### API request sent internally
|
|
1204
|
-
|
|
1205
|
-
```json
|
|
1206
|
-
{
|
|
1207
|
-
"Data": {
|
|
1208
|
-
"UserTestResultId": "<USER_TEST_RESULT_ID>"
|
|
1209
|
-
}
|
|
593
|
+
const r = await safeCdx.getResultDetails(userTestResultId);
|
|
594
|
+
if (r.success) {
|
|
595
|
+
const details = r.data; // TestResultDetails
|
|
1210
596
|
}
|
|
1211
597
|
```
|
|
1212
598
|
|
|
1213
|
-
|
|
599
|
+
Example response:
|
|
1214
600
|
|
|
1215
601
|
```json
|
|
1216
602
|
{
|
|
1217
603
|
"success": true,
|
|
1218
604
|
"data": {
|
|
1219
|
-
"
|
|
1220
|
-
"userID": "5d00527a-2a9c-4d68-a7f4-8f154b2ca3e6",
|
|
1221
|
-
"tenantID": "healthcheck",
|
|
1222
|
-
"isSelfAssessment": false,
|
|
605
|
+
"_id": "6a104bba6068dd9a213db810",
|
|
1223
606
|
"status": "Started",
|
|
1224
607
|
"cvmlStatus": "Retake",
|
|
1225
608
|
"gtin": "860002060439",
|
|
1226
|
-
"testInfoHeader": "Winx Health",
|
|
1227
609
|
"testName": "Winx UTI Test + Treat",
|
|
1228
|
-
"imageOfCapture": "860002060439_6a104bba6068dd9a213db810-1.jpg",
|
|
1229
|
-
"imageOfCaptureUploadAt": "2026-05-22T12:31:18.333Z",
|
|
1230
|
-
"imageOfCaptureResults": [
|
|
1231
|
-
{
|
|
1232
|
-
"imageOfCapture": "860002060439_6a104bba6068dd9a213db810-1.jpg",
|
|
1233
|
-
"uploadAt": "2026-05-22T12:31:18.333Z",
|
|
1234
|
-
"cvmlStatus": "Retake",
|
|
1235
|
-
"parsed": {
|
|
1236
|
-
"outcome": "Retake",
|
|
1237
|
-
"responseCode": "Err4",
|
|
1238
|
-
"responseMessage": "ml_result_code_error_04_msg",
|
|
1239
|
-
"responseTitle": "ml_result_code_error_04_title"
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
],
|
|
1243
|
-
"result": [],
|
|
1244
|
-
"recordedDate": "2026-05-22T12:27:38.731Z",
|
|
1245
|
-
"cvmlTestName": "winx-uti",
|
|
1246
|
-
"isCVMLBackground": false,
|
|
1247
|
-
"isCVMLResult": true,
|
|
1248
|
-
"isSelfReportingPostTest": false,
|
|
1249
|
-
"isSelfReportingPreTest": false,
|
|
1250
|
-
"viewed": false,
|
|
1251
610
|
"finalized": false,
|
|
1252
|
-
"
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
"retryLimit": 2,
|
|
1256
|
-
"failoverEnabled": true,
|
|
1257
|
-
"lastCvmlStatus": "Retake",
|
|
1258
|
-
"failoverTriggered": false
|
|
1259
|
-
},
|
|
1260
|
-
"questionnaireSnapshot": {
|
|
1261
|
-
"isValid": [],
|
|
1262
|
-
"analyte": [
|
|
1263
|
-
{
|
|
1264
|
-
"_id": "1",
|
|
1265
|
-
"displayOrder": 1,
|
|
1266
|
-
"display": "CVML",
|
|
1267
|
-
"name": "Leukocyte",
|
|
1268
|
-
"question": "What is the Leukocyte reading?",
|
|
1269
|
-
"image": "WinxLeuk.png",
|
|
1270
|
-
"cvmlOrder": 1,
|
|
1271
|
-
"responses": [
|
|
1272
|
-
{
|
|
1273
|
-
"value": "Negative -",
|
|
1274
|
-
"displayOrder": 1,
|
|
1275
|
-
"storedValue": "Neg",
|
|
1276
|
-
"cvmlValue": "Neg",
|
|
1277
|
-
"score": "0"
|
|
1278
|
-
},
|
|
1279
|
-
{
|
|
1280
|
-
"value": "Trace 15+-",
|
|
1281
|
-
"displayOrder": 2,
|
|
1282
|
-
"storedValue": "15+-",
|
|
1283
|
-
"cvmlValue": "15+-",
|
|
1284
|
-
"score": "3"
|
|
1285
|
-
},
|
|
1286
|
-
{
|
|
1287
|
-
"value": "Positive 70+",
|
|
1288
|
-
"displayOrder": 3,
|
|
1289
|
-
"storedValue": "70+",
|
|
1290
|
-
"cvmlValue": "70+",
|
|
1291
|
-
"score": "4"
|
|
1292
|
-
}
|
|
1293
|
-
]
|
|
1294
|
-
},
|
|
1295
|
-
{
|
|
1296
|
-
"_id": "2",
|
|
1297
|
-
"displayOrder": 2,
|
|
1298
|
-
"display": "CVML",
|
|
1299
|
-
"name": "Nitrite",
|
|
1300
|
-
"question": "What is the Nitrite reading?",
|
|
1301
|
-
"image": "WinxNitr.png",
|
|
1302
|
-
"cvmlOrder": 2,
|
|
1303
|
-
"responses": [
|
|
1304
|
-
{
|
|
1305
|
-
"value": "Negative",
|
|
1306
|
-
"displayOrder": 1,
|
|
1307
|
-
"storedValue": "Neg",
|
|
1308
|
-
"cvmlValue": "Neg",
|
|
1309
|
-
"score": "0"
|
|
1310
|
-
},
|
|
1311
|
-
{
|
|
1312
|
-
"value": "Positive Low+",
|
|
1313
|
-
"displayOrder": 2,
|
|
1314
|
-
"storedValue": "Low+",
|
|
1315
|
-
"cvmlValue": "Low+",
|
|
1316
|
-
"score": "2"
|
|
1317
|
-
},
|
|
1318
|
-
{
|
|
1319
|
-
"value": "Positive High++",
|
|
1320
|
-
"displayOrder": 3,
|
|
1321
|
-
"storedValue": "High++",
|
|
1322
|
-
"cvmlValue": "High++",
|
|
1323
|
-
"score": "2"
|
|
1324
|
-
}
|
|
1325
|
-
]
|
|
1326
|
-
}
|
|
1327
|
-
]
|
|
1328
|
-
},
|
|
1329
|
-
"resumed": false,
|
|
1330
|
-
"language": "en",
|
|
1331
|
-
"labVendor": "Winx Health",
|
|
1332
|
-
"labTestType": "Home Test (Rapid)",
|
|
1333
|
-
"statusResultsText": "",
|
|
1334
|
-
"externalId": "test-user@example.com",
|
|
1335
|
-
"overallResult": "Unknown",
|
|
1336
|
-
"positiveAnalytes": [],
|
|
1337
|
-
"_id": "6a104bba6068dd9a213db810",
|
|
1338
|
-
"modifier": "system",
|
|
1339
|
-
"created": "2026-05-22T12:27:38.731Z",
|
|
1340
|
-
"modified": "2026-05-22T12:33:17.308Z"
|
|
611
|
+
"result": [
|
|
612
|
+
{ "analyte": "Purchase", "reportedValue": "drug store", "storedValue": "drug store", "score": "0" }
|
|
613
|
+
]
|
|
1341
614
|
},
|
|
1342
615
|
"message": "Success",
|
|
1343
616
|
"code": 0
|
|
@@ -1346,51 +619,30 @@ const details = await safeCdx.getResultDetails(userTestResultId);
|
|
|
1346
619
|
|
|
1347
620
|
---
|
|
1348
621
|
|
|
1349
|
-
### `getResultPdf(
|
|
1350
|
-
|
|
1351
|
-
Requests the PDF result for a specific test attempt.
|
|
622
|
+
### `getResultPdf(userTestResultId)` — POST
|
|
1352
623
|
|
|
1353
|
-
|
|
624
|
+
Returns a pre-signed S3 URL for the PDF result. Returns **Shape B**. `Data.data` is the URL string.
|
|
1354
625
|
|
|
1355
626
|
```ts
|
|
1356
|
-
safeCdx.getResultPdf(
|
|
627
|
+
safeCdx.getResultPdf(
|
|
628
|
+
userTestResultId: string
|
|
629
|
+
): Promise<APIResponse<SafeAPIResponse<string>>>
|
|
1357
630
|
```
|
|
1358
631
|
|
|
1359
|
-
`GetResultPdfData` is:
|
|
1360
|
-
|
|
1361
632
|
```ts
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
#### Parameters
|
|
1366
|
-
|
|
1367
|
-
| Parameter | Type | Required | Description |
|
|
1368
|
-
|---|---|---:|---|
|
|
1369
|
-
| `userTestResultId` | `string` | Yes | Identifier of the test attempt. |
|
|
1370
|
-
|
|
1371
|
-
#### Usage
|
|
1372
|
-
|
|
1373
|
-
```ts
|
|
1374
|
-
const pdf = await safeCdx.getResultPdf(userTestResultId);
|
|
1375
|
-
```
|
|
1376
|
-
|
|
1377
|
-
#### API request sent internally
|
|
1378
|
-
|
|
1379
|
-
```json
|
|
1380
|
-
{
|
|
1381
|
-
"Data": {
|
|
1382
|
-
"UserTestResultId": "<USER_TEST_RESULT_ID>"
|
|
1383
|
-
}
|
|
633
|
+
const r = await safeCdx.getResultPdf(userTestResultId);
|
|
634
|
+
if (r.IsOK && r.Data?.success) {
|
|
635
|
+
const pdfUrl = r.Data.data; // string — pre-signed S3 URL
|
|
1384
636
|
}
|
|
1385
637
|
```
|
|
1386
638
|
|
|
1387
|
-
|
|
639
|
+
Example response:
|
|
1388
640
|
|
|
1389
641
|
```json
|
|
1390
642
|
{
|
|
1391
643
|
"Data": {
|
|
1392
644
|
"success": true,
|
|
1393
|
-
"data": "https://safe-manual-test-results-us-west-2-quality-speed.s3.us-west-2.amazonaws.com/archive/2026/05/22/12/48/7ecfd730-c4c3-4cd6-8b12-4210b9a2db28.pdf?X-Amz-Expires=900
|
|
645
|
+
"data": "https://safe-manual-test-results-us-west-2-quality-speed.s3.us-west-2.amazonaws.com/archive/2026/05/22/12/48/7ecfd730-c4c3-4cd6-8b12-4210b9a2db28.pdf?X-Amz-Expires=900&...",
|
|
1394
646
|
"message": "Success",
|
|
1395
647
|
"code": 0
|
|
1396
648
|
},
|
|
@@ -1401,53 +653,29 @@ const pdf = await safeCdx.getResultPdf(userTestResultId);
|
|
|
1401
653
|
|
|
1402
654
|
---
|
|
1403
655
|
|
|
1404
|
-
### `getImageCaptureUrl(
|
|
1405
|
-
|
|
1406
|
-
Requests image capture data for a specific test attempt. This endpoint returns `SafeAPIResponse<T>` directly.
|
|
656
|
+
### `getImageCaptureUrl(userTestResultId)` — POST
|
|
1407
657
|
|
|
1408
|
-
|
|
658
|
+
Returns image capture metadata for a test attempt. Returns **Shape C**.
|
|
1409
659
|
|
|
1410
660
|
```ts
|
|
1411
661
|
safeCdx.getImageCaptureUrl(
|
|
1412
|
-
|
|
1413
|
-
): Promise<GetImageCaptureUrlResponse>
|
|
662
|
+
userTestResultId: string
|
|
663
|
+
): Promise<GetImageCaptureUrlResponse> // = SafeAPIResponse<string>
|
|
1414
664
|
```
|
|
1415
665
|
|
|
1416
|
-
`GetImageCaptureUrlResponse` is:
|
|
1417
|
-
|
|
1418
666
|
```ts
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
#### Parameters
|
|
1424
|
-
|
|
1425
|
-
| Parameter | Type | Required | Description |
|
|
1426
|
-
|---|---|---:|---|
|
|
1427
|
-
| `userTestResultId` | `string` | Yes | Identifier of the test attempt. |
|
|
1428
|
-
|
|
1429
|
-
#### Usage
|
|
1430
|
-
|
|
1431
|
-
```ts
|
|
1432
|
-
const imageUrl = await safeCdx.getImageCaptureUrl(userTestResultId);
|
|
1433
|
-
```
|
|
1434
|
-
|
|
1435
|
-
#### API request sent internally
|
|
1436
|
-
|
|
1437
|
-
```json
|
|
1438
|
-
{
|
|
1439
|
-
"Data": {
|
|
1440
|
-
"UserTestResultId": "<USER_TEST_RESULT_ID>"
|
|
1441
|
-
}
|
|
667
|
+
const r = await safeCdx.getImageCaptureUrl(userTestResultId);
|
|
668
|
+
if (r.success) {
|
|
669
|
+
const imageUrl = r.data; // string — pre-signed URL
|
|
1442
670
|
}
|
|
1443
671
|
```
|
|
1444
672
|
|
|
1445
|
-
|
|
673
|
+
Example response:
|
|
1446
674
|
|
|
1447
675
|
```json
|
|
1448
676
|
{
|
|
1449
677
|
"success": true,
|
|
1450
|
-
"data": "https://sf-us-west-2-lower-neural-cdx-img-uploads-quality.s3.us-west-2.amazonaws.com/healthcheck/winx-uti
|
|
678
|
+
"data": "https://sf-us-west-2-lower-neural-cdx-img-uploads-quality.s3.us-west-2.amazonaws.com/healthcheck/winx-uti/...?X-Amz-Expires=900&...",
|
|
1451
679
|
"message": "Success",
|
|
1452
680
|
"code": 0
|
|
1453
681
|
}
|
|
@@ -1455,70 +683,40 @@ const imageUrl = await safeCdx.getImageCaptureUrl(userTestResultId);
|
|
|
1455
683
|
|
|
1456
684
|
---
|
|
1457
685
|
|
|
1458
|
-
### `resumeFlow(
|
|
686
|
+
### `resumeFlow(userTestResultId, resumed?)` — POST
|
|
1459
687
|
|
|
1460
|
-
Marks a test attempt as resumed or not
|
|
688
|
+
Marks a test attempt as resumed or not. Returns **Shape B**.
|
|
1461
689
|
|
|
1462
|
-
|
|
690
|
+
| Parameter | Type | Default | Description |
|
|
691
|
+
|---|---|---|---|
|
|
692
|
+
| `userTestResultId` | `string` | — | Test attempt identifier |
|
|
693
|
+
| `resumed` | `boolean` | `true` | Resume state to set |
|
|
1463
694
|
|
|
1464
695
|
```ts
|
|
1465
696
|
safeCdx.resumeFlow(
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
): Promise<APIResponse<
|
|
697
|
+
userTestResultId: string,
|
|
698
|
+
resumed?: boolean
|
|
699
|
+
): Promise<APIResponse<SafeAPIResponse<ResumeFlowResult>>>
|
|
1469
700
|
```
|
|
1470
701
|
|
|
1471
|
-
`ResumeFlowData` is:
|
|
1472
|
-
|
|
1473
|
-
```ts
|
|
1474
|
-
type ResumeFlowData =
|
|
1475
|
-
SafeAPIResponse<ResumeFlowResult>;
|
|
1476
|
-
```
|
|
1477
|
-
|
|
1478
|
-
#### Parameters
|
|
1479
|
-
|
|
1480
|
-
| Parameter | Type | Required | Description |
|
|
1481
|
-
|---|---|---:|---|
|
|
1482
|
-
| `userTestResultId` | `string` | Yes | Identifier of the test attempt. |
|
|
1483
|
-
| `resumed` | `boolean` | No | Resume state sent to the API. Defaults to `true`. |
|
|
1484
|
-
|
|
1485
|
-
#### Usage
|
|
1486
|
-
|
|
1487
702
|
```ts
|
|
1488
|
-
const
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
```ts
|
|
1492
|
-
const response = await safeCdx.resumeFlow(
|
|
1493
|
-
userTestResultId,
|
|
1494
|
-
true
|
|
1495
|
-
);
|
|
1496
|
-
```
|
|
1497
|
-
|
|
1498
|
-
#### API request sent internally
|
|
1499
|
-
|
|
1500
|
-
```json
|
|
1501
|
-
{
|
|
1502
|
-
"Data": {
|
|
1503
|
-
"UserTestResultId": "<USER_TEST_RESULT_ID>",
|
|
1504
|
-
"Resumed": true
|
|
1505
|
-
}
|
|
703
|
+
const r = await safeCdx.resumeFlow(userTestResultId, true);
|
|
704
|
+
if (r.IsOK && r.Data?.success) {
|
|
705
|
+
const result = r.Data.data; // ResumeFlowResult
|
|
1506
706
|
}
|
|
1507
707
|
```
|
|
1508
708
|
|
|
1509
|
-
|
|
709
|
+
Example response:
|
|
1510
710
|
|
|
1511
711
|
```json
|
|
1512
712
|
{
|
|
1513
713
|
"Data": {
|
|
1514
714
|
"success": true,
|
|
1515
715
|
"data": {
|
|
716
|
+
"_id": "6a02f6da6068dd9a213db7cb",
|
|
1516
717
|
"cvmlStatus": "Retake",
|
|
1517
|
-
"reportType": "SELF",
|
|
1518
718
|
"resumed": true,
|
|
1519
|
-
"failoverTriggered": false
|
|
1520
|
-
"showValidity": false,
|
|
1521
|
-
"_id": "6a02f6da6068dd9a213db7cb"
|
|
719
|
+
"failoverTriggered": false
|
|
1522
720
|
},
|
|
1523
721
|
"message": "Success",
|
|
1524
722
|
"code": 0
|
|
@@ -1530,75 +728,40 @@ const response = await safeCdx.resumeFlow(
|
|
|
1530
728
|
|
|
1531
729
|
---
|
|
1532
730
|
|
|
1533
|
-
### `submitAnswers(
|
|
731
|
+
### `submitAnswers(submission)` — POST
|
|
1534
732
|
|
|
1535
|
-
Submits analyte answers for a test attempt.
|
|
733
|
+
Submits analyte answers for a test attempt. Returns **Shape B**.
|
|
1536
734
|
|
|
1537
|
-
|
|
735
|
+
| Parameter | Type | Required | Description |
|
|
736
|
+
|---|---|---|---|
|
|
737
|
+
| `submission.UserTestResultId` | `string` | Yes | Test attempt identifier |
|
|
738
|
+
| `submission.Result` | `AnswerResult[]` | Yes | At least one answer required |
|
|
1538
739
|
|
|
1539
740
|
```ts
|
|
1540
741
|
safeCdx.submitAnswers(
|
|
1541
|
-
|
|
1542
|
-
): Promise<APIResponse<
|
|
1543
|
-
```
|
|
1544
|
-
|
|
1545
|
-
`SubmitAnswersData` is:
|
|
1546
|
-
|
|
1547
|
-
```ts
|
|
1548
|
-
type SubmitAnswersData =
|
|
1549
|
-
SafeAPIResponse<EntityReferenceData>;
|
|
742
|
+
submission: SubmitAnswersRequest
|
|
743
|
+
): Promise<APIResponse<SafeAPIResponse<EntityReferenceData>>>
|
|
1550
744
|
```
|
|
1551
745
|
|
|
1552
|
-
#### Parameters
|
|
1553
|
-
|
|
1554
|
-
| Parameter | Type | Required | Description |
|
|
1555
|
-
|---|---|---:|---|
|
|
1556
|
-
| `submission.UserTestResultId` | `string` | Yes | Identifier of the test attempt. |
|
|
1557
|
-
| `submission.Result` | `AnswerResult[]` | Yes | Answer entries submitted for the test attempt. |
|
|
1558
|
-
|
|
1559
|
-
#### Usage
|
|
1560
|
-
|
|
1561
746
|
```ts
|
|
1562
|
-
const
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
ReportedValue: "drug store",
|
|
1568
|
-
StoredValue: "drug store",
|
|
1569
|
-
Score: "0"
|
|
1570
|
-
}
|
|
1571
|
-
]
|
|
747
|
+
const r = await safeCdx.submitAnswers({
|
|
748
|
+
UserTestResultId: userTestResultId,
|
|
749
|
+
Result: [
|
|
750
|
+
{ Analyte: "Purchase", ReportedValue: "drug store", StoredValue: "drug store", Score: "0" }
|
|
751
|
+
]
|
|
1572
752
|
});
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
#### API request sent internally
|
|
1576
|
-
|
|
1577
|
-
```json
|
|
1578
|
-
{
|
|
1579
|
-
"Data": {
|
|
1580
|
-
"UserTestResultId": "<USER_TEST_RESULT_ID>",
|
|
1581
|
-
"Result": [
|
|
1582
|
-
{
|
|
1583
|
-
"Analyte": "Purchase",
|
|
1584
|
-
"ReportedValue": "drug store",
|
|
1585
|
-
"StoredValue": "drug store",
|
|
1586
|
-
"Score": "0"
|
|
1587
|
-
}
|
|
1588
|
-
]
|
|
1589
|
-
}
|
|
753
|
+
if (r.IsOK && r.Data?.success) {
|
|
754
|
+
const id = r.Data.data?._id;
|
|
1590
755
|
}
|
|
1591
756
|
```
|
|
1592
757
|
|
|
1593
|
-
|
|
758
|
+
Example response:
|
|
1594
759
|
|
|
1595
760
|
```json
|
|
1596
761
|
{
|
|
1597
762
|
"Data": {
|
|
1598
763
|
"success": true,
|
|
1599
|
-
"data": {
|
|
1600
|
-
"_id": "6a104bba6068dd9a213db810"
|
|
1601
|
-
},
|
|
764
|
+
"data": { "_id": "6a104bba6068dd9a213db810" },
|
|
1602
765
|
"message": "Success",
|
|
1603
766
|
"code": 0
|
|
1604
767
|
},
|
|
@@ -1609,82 +772,48 @@ const response = await safeCdx.submitAnswers({
|
|
|
1609
772
|
|
|
1610
773
|
---
|
|
1611
774
|
|
|
1612
|
-
### `finalizeTest(
|
|
775
|
+
### `finalizeTest(userTestResultId)` — POST
|
|
1613
776
|
|
|
1614
|
-
Finalizes
|
|
777
|
+
Finalizes a test attempt. Returns **Shape A**.
|
|
1615
778
|
|
|
1616
|
-
|
|
779
|
+
`Data` is typed as `unknown`, so treat any returned payload as illustrative rather than a stable SDK contract. Check `r.IsOK` for success.
|
|
1617
780
|
|
|
1618
781
|
```ts
|
|
1619
|
-
safeCdx.finalizeTest(userTestResultId: string): Promise<APIResponse<
|
|
782
|
+
safeCdx.finalizeTest(userTestResultId: string): Promise<APIResponse<unknown>>
|
|
1620
783
|
```
|
|
1621
784
|
|
|
1622
|
-
#### Parameters
|
|
1623
|
-
|
|
1624
|
-
| Parameter | Type | Required | Description |
|
|
1625
|
-
|---|---|---:|---|
|
|
1626
|
-
| `userTestResultId` | `string` | Yes | Identifier of the test attempt to finalize. |
|
|
1627
|
-
|
|
1628
|
-
#### Usage
|
|
1629
|
-
|
|
1630
785
|
```ts
|
|
1631
|
-
const
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
#### API request sent internally
|
|
1635
|
-
|
|
1636
|
-
```json
|
|
1637
|
-
{
|
|
1638
|
-
"Data": {
|
|
1639
|
-
"UserTestResultId": "<USER_TEST_RESULT_ID>"
|
|
1640
|
-
}
|
|
786
|
+
const r = await safeCdx.finalizeTest(userTestResultId);
|
|
787
|
+
if (r.IsOK) {
|
|
788
|
+
// test finalized
|
|
1641
789
|
}
|
|
1642
790
|
```
|
|
1643
791
|
|
|
1644
|
-
|
|
1645
|
-
## Distinct API Host
|
|
1646
|
-
|
|
1647
|
-
Safe CDX calls a **different backend service** from all other connectors. The host is `api-hcs.healthcloud-services.com` (not `api-healthcheck.healthcloud-services.com`). The URL is derived from `loginClient.getEnvironment()`:
|
|
1648
|
-
|
|
1649
|
-
| Environment | Host |
|
|
1650
|
-
| --- | --- |
|
|
1651
|
-
| `dev` | `https://dev-api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
|
|
1652
|
-
| `uat` | `https://uat-api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
|
|
1653
|
-
| `prod` | `https://api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
|
|
1654
|
-
|
|
1655
|
-
`loginClient.getBaseUrl()` is **not used** by this connector — it points to the Healthcheck API, not SafeCDX.
|
|
1656
|
-
|
|
1657
|
-
---
|
|
1658
|
-
|
|
1659
|
-
## Response Type by Method
|
|
1660
|
-
|
|
1661
|
-
| Method | Returns |
|
|
1662
|
-
| --- | --- |
|
|
1663
|
-
| `getTestProfileByGTIN(gtin)` | `APIResponse<GetTestProfileByGTINData>` |
|
|
1664
|
-
| `listTestProfilesByAccount(...)` | `SafeAPIResponse<TestProfileByAccountItem[]>` |
|
|
1665
|
-
| `createUploadUrl(...)` | `APIResponse<CreateUploadUrlData>` |
|
|
1666
|
-
| `uploadImage(preSignedURL, image, contentType?)` | `Promise<void>` — direct S3 upload, no envelope |
|
|
1667
|
-
| `updateCvmlStatus(...)` | `SafeAPIResponse<UpdateCvmlStatusResponse>` |
|
|
1668
|
-
| `getCvmlResults(imageOfCaptureId)` | `SafeAPIResponse<GetCvmlResultsResponse>` |
|
|
1669
|
-
| `listPendingResults(excludeStatus?)` | `APIResponse<GetPendingResultsData>` |
|
|
1670
|
-
| `getLastResults(excludeStatus?)` | `APIResponse<GetLastResultsData>` |
|
|
1671
|
-
| `listTestHistory(excludeStatus?)` | `APIResponse<GetTestHistoryData>` |
|
|
1672
|
-
| `getResultDetails(userTestResultId)` | `SafeAPIResponse<GetResultDetailsResponse>` |
|
|
1673
|
-
| `getResultPdf(userTestResultId)` | `APIResponse<GetResultPdfData>` |
|
|
1674
|
-
| `getImageCaptureUrl(userTestResultId)` | `SafeAPIResponse<GetImageCaptureUrlResponse>` |
|
|
1675
|
-
| `resumeFlow(userTestResultId, resumed?)` | `APIResponse<ResumeFlowData>` |
|
|
1676
|
-
| `submitAnswers(submission)` | `APIResponse<SubmitAnswersData>` |
|
|
1677
|
-
| `finalizeTest(userTestResultId)` | `APIResponse<FinalizeTestData>` |
|
|
1678
|
-
|
|
1679
|
-
`uploadImage` bypasses the `HttpClient` entirely and calls the pre-signed storage URL directly using the native `fetch` API. Auth headers are **not** sent — the pre-signed URL provides its own short-lived credential.
|
|
1680
|
-
|
|
1681
792
|
---
|
|
1682
793
|
|
|
1683
|
-
##
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
794
|
+
## Method Summary
|
|
795
|
+
|
|
796
|
+
| Method | Shape | Return type | Access data via |
|
|
797
|
+
|---|---|---|---|
|
|
798
|
+
| `getTestProfileByGTIN(gtin)` | A | `APIResponse<GetTestProfileByGTINData>` | `r.Data` |
|
|
799
|
+
| `listTestProfilesByAccount(...)` | B | `APIResponse<SafeAPIResponse<TestProfileByAccountItem[]>>` | `r.Data?.data` |
|
|
800
|
+
| `createUploadUrl(...)` | A | `APIResponse<CreateUploadUrlData>` | `r.Data` |
|
|
801
|
+
| `uploadImage(...)` | — | `Promise<void>` | throws on failure |
|
|
802
|
+
| `updateCvmlStatus(...)` | C | `SafeAPIResponse<EntityReferenceData>` | `r.data` |
|
|
803
|
+
| `getCvmlResults(imageOfCaptureId)` | C | `SafeAPIResponse<CvmlResultsData>` | `r.data` |
|
|
804
|
+
| `listPendingResults(...)` | B | `APIResponse<SafeAPIResponse<TestResultSummary[]>>` | `r.Data?.data` |
|
|
805
|
+
| `getLastResults(...)` | B | `APIResponse<SafeAPIResponse<TestResultDetails>>` | `r.Data?.data` |
|
|
806
|
+
| `listTestHistory(...)` | B | `APIResponse<SafeAPIResponse<TestResultSummary[]>>` | `r.Data?.data` |
|
|
807
|
+
| `getResultDetails(userTestResultId)` | C | `SafeAPIResponse<TestResultDetails>` | `r.data` |
|
|
808
|
+
| `getResultPdf(userTestResultId)` | B | `APIResponse<SafeAPIResponse<string>>` | `r.Data?.data` |
|
|
809
|
+
| `getImageCaptureUrl(userTestResultId)` | C | `SafeAPIResponse<string>` | `r.data` |
|
|
810
|
+
| `resumeFlow(...)` | B | `APIResponse<SafeAPIResponse<ResumeFlowResult>>` | `r.Data?.data` |
|
|
811
|
+
| `submitAnswers(submission)` | B | `APIResponse<SafeAPIResponse<EntityReferenceData>>` | `r.Data?.data` |
|
|
812
|
+
| `finalizeTest(userTestResultId)` | A | `APIResponse<unknown>` | `r.IsOK` |
|
|
813
|
+
|
|
814
|
+
**Shape legend:**
|
|
815
|
+
- **A** — `APIResponse<T>` — check `r.IsOK`, read `r.Data`
|
|
816
|
+
- **B** — `APIResponse<SafeAPIResponse<T>>` — check `r.IsOK && r.Data?.success`, read `r.Data.data`
|
|
817
|
+
- **C** — `SafeAPIResponse<T>` — check `r.success`, read `r.data`
|
|
818
|
+
|
|
819
|
+
> Shape B is a backend pass-through: the Health Cloud gateway returns the SafeCDX vendor response as-is inside `APIResponse.Data`, without normalising it. `r.IsOK` and `r.Data?.success` are independent checks — both must be true before reading `r.Data.data`.
|