# DX365 Measurement Results — API Documentation

**Base URL:** `https://msm9xyearn.eu-central-1.awsapprunner.com`
**Version:** 1.0
**Last updated:** 2026-03-27

---

## Authentication

All API requests require a Bearer token in the `Authorization` header:

```
Authorization: Bearer <your-api-key>
```

- The API key is issued by us and sent to you directly.
- API keys **do not expire** and do not require periodic rotation.
- If you suspect a key has been compromised, contact us for an immediate replacement.
- Keep the key secret — do not embed it in client-side code or public repositories.

---

## Endpoints

### 1. Get Current User Info

```
GET /api/me
```

Verify your credentials and see which partner scope you belong to.

**Response `200`:**
```json
{
  "email": "user@company.com",
  "partnerId": "your-partner-id"
}
```

**Response `401`:**
```json
{
  "error": "Unauthorized"
}
```

---

### 2. List Files

```
GET /api/files
```

Lists folders (devices) and files within your partner scope.

#### Parameters

| Parameter | Type   | Required | Default | Description |
|-----------|--------|----------|---------|-------------|
| `prefix`  | string | no       | `""`    | Relative path within your scope. Use a device ID followed by `/` to list files for a specific device. |
| `sort`    | string | no       | `name`  | Sort order for files: `newest`, `oldest`, or `name`. Folders always appear first. |
| `since`   | string | no       | —       | ISO 8601 timestamp. Only return files modified **after** this date. Useful for incremental polling. |

#### Examples

**List all devices:**
```bash
curl -H "Authorization: Bearer sk_..." \
  "https://msm9xyearn.eu-central-1.awsapprunner.com/api/files"
```

```json
{
  "items": [
    {
      "type": "folder",
      "name": "010426247918003401S0DA2446000003",
      "prefix": "010426247918003401S0DA2446000003/"
    }
  ],
  "prefix": ""
}
```

**List files for a specific device (newest first):**
```bash
curl -H "Authorization: Bearer sk_..." \
  "https://msm9xyearn.eu-central-1.awsapprunner.com/api/files?prefix=010426247918003401S0DA2446000003/&sort=newest"
```

```json
{
  "items": [
    {
      "type": "file",
      "name": "1734468676.json",
      "key": "010426247918003401S0DA2446000003/1734468676.json",
      "size": 464,
      "lastModified": "2026-03-27T12:21:58.000Z"
    }
  ],
  "prefix": "010426247918003401S0DA2446000003/"
}
```

**Incremental polling (only new files since last check):**
```bash
curl -H "Authorization: Bearer sk_..." \
  "https://msm9xyearn.eu-central-1.awsapprunner.com/api/files?prefix=010426247918003401S0DA2446000003/&sort=oldest&since=2026-03-27T12:21:58.000Z"
```

---

### 3. Get File Content

```
GET /api/files/preview
```

Returns the content of a file. JSON files are returned as **parsed objects** — no extra parsing needed.

#### Parameters

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| `key`     | string | yes      | Relative file path from the list response (the `key` field). |

#### Example

```bash
curl -H "Authorization: Bearer sk_..." \
  "https://msm9xyearn.eu-central-1.awsapprunner.com/api/files/preview?key=010426247918003401S0DA2446000003/1734468676.json"
```

**Response `200`:**
```json
{
  "content": {
    "fileName": "1734468676.json",
    "data": {
      "patientId": "6545",
      "sex": "male",
      "age": "0",
      "timestamp": 1734468676,
      "testName": "D-Dimer",
      "testSerialNumber": "0",
      "lotNumber": "2023102305",
      "refNumber": "A1C5050200",
      "assays": [
        {
          "codeInterpretation": 46,
          "units": 77,
          "loincCode": "91556-1",
          "result": "1.8",
          "interpretationCount": 1,
          "interpretations": [
            {
              "refMin": "0",
              "refMax": "0.5",
              "refInterpretation": 46
            }
          ]
        }
      ]
    },
    "deviceId": "010426247918003401S0DA2446000003"
  }
}
```

---

### 4. Download File

```
GET /api/files/download
```

Returns a temporary presigned URL for direct file download. The URL is valid for **5 minutes**.

#### Parameters

| Parameter | Type   | Required | Description |
|-----------|--------|----------|-------------|
| `key`     | string | yes      | Relative file path from the list response. |

#### Example

```bash
curl -H "Authorization: Bearer sk_..." \
  "https://msm9xyearn.eu-central-1.awsapprunner.com/api/files/download?key=010426247918003401S0DA2446000003/1734468676.json"
```

**Response `200`:**
```json
{
  "url": "https://results-measurements.s3.eu-central-1.amazonaws.com/..."
}
```

Then download the file directly:
```bash
curl -o result.json "<presigned-url>"
```

---

### 5. Health Check

```
GET /api/health
```

No authentication required.

**Response `200`:**
```json
{
  "status": "ok"
}
```

---

## Error Responses

All errors follow the same format:

```json
{
  "error": "Error description"
}
```

| HTTP Code | Meaning |
|-----------|---------|
| `400`     | Bad request — missing or invalid parameters |
| `401`     | Unauthorized — missing or invalid API key |
| `403`     | Forbidden — trying to access files outside your scope |
| `500`     | Internal server error |

---

## Recommended Integration Flow

### Initial sync

1. Call `GET /api/files` to get the list of device folders.
2. For each device, call `GET /api/files?prefix=<deviceId>/&sort=oldest` to get all files.
3. For each file, call `GET /api/files/preview?key=<file-key>` to read the content.
4. Store the `lastModified` value of the last processed file.

### Incremental polling

1. Call `GET /api/files?prefix=<deviceId>/&sort=oldest&since=<lastModified>` to get only new files.
2. Process the files.
3. Update your stored `lastModified` with the value from the newest processed file.

**Recommended polling interval:** ≥ 60 seconds.

### Handling processed files

Files are **read-only** and remain in the system permanently. There is no delete or mark-as-read mechanism. Track processed files on your side using the `key` or `lastModified` values.

---

## Data Model Reference

### File list item (type: `folder`)

| Field    | Type   | Description |
|----------|--------|-------------|
| `type`   | string | Always `"folder"` |
| `name`   | string | Folder name (typically a device ID) |
| `prefix` | string | Path to use as `prefix` parameter |

### File list item (type: `file`)

| Field          | Type   | Description |
|----------------|--------|-------------|
| `type`         | string | Always `"file"` |
| `name`         | string | File name |
| `key`          | string | Path to use as `key` parameter for preview/download |
| `size`         | number | File size in bytes |
| `lastModified` | string | ISO 8601 timestamp of last modification |

### Measurement file content

| Field                        | Type   | Description |
|------------------------------|--------|-------------|
| `fileName`                   | string | Original file name |
| `deviceId`                   | string | Device serial number |
| `data.patientId`             | string | Patient identifier |
| `data.sex`                   | string | `"male"` or `"female"` |
| `data.age`                   | string | Patient age |
| `data.timestamp`             | number | Unix timestamp of the measurement |
| `data.testName`              | string | Test type (e.g. `"D-Dimer"`, `"CRP"`, `"HbA1c"`) |
| `data.lotNumber`             | string | Reagent lot number |
| `data.refNumber`             | string | Reagent reference number |
| `data.assays[]`              | array  | Array of assay results |
| `data.assays[].result`       | string | Measured value |
| `data.assays[].units`        | number | Unit code (see mapping table below) |
| `data.assays[].loincCode`    | string | LOINC code for the analyte |
| `data.assays[].codeInterpretation` | number | Interpretation code (see mapping table below) |
| `data.assays[].interpretations[]` | array | Reference range interpretations |
| `data.assays[].interpretations[].refMin` | string | Reference range minimum |
| `data.assays[].interpretations[].refMax` | string | Reference range maximum |
| `data.assays[].interpretations[].refInterpretation` | number | Interpretation code for the reference range |

---

## Mapping Tables

### Units (`units` field)

The `units` field in assay results is a numeric code. Use the table below to resolve it to a human-readable unit.

| Code | Unit | Description |
|------|------|-------------|
| 1 | % | Percent |
| 2 | /uL | Per microliter |
| 3 | IU/L | International Units per liter |
| 6 | g/dL | Grams per deciliter |
| 7 | g/L | Grams per liter |
| 8 | g/mL | Grams per milliliter |
| 9 | mIU/mL | MilliInternational Units per milliliter |
| 10 | mEq/L | MilliEquivalents per liter |
| 11 | mg/dL | Milligrams per deciliter |
| 12 | mmol/L | Millimoles per liter |
| 13 | ng/mL | Nanograms per milliliter |
| 14 | nmol/L | Nanomoles per liter |
| 15 | pg/mL | Picograms per milliliter |
| 16 | pmol/L | Picomoles per liter |
| 17 | U/L | Units per liter |
| 18 | uIU/mL | MicroInternational Units per milliliter |
| 19 | ug/dL | Micrograms per deciliter |
| 20 | ug/L | Micrograms per liter |
| 21 | ug/mL | Micrograms per milliliter |
| 22 | umol/L | Micromoles per liter |
| 24 | mIU/mL | MilliInternational Units per milliliter |
| 29 | Elisa U/mL | Elisa Units per milliliter |
| 30 | IU/mL | International Units per milliliter |
| 31 | kIU/mL | KiloInternational Units per milliliter |
| 32 | IU/dL | International Units per deciliter |
| 33 | EU/dL | Ehrlich Units per deciliter |
| 34 | mIU/L | MilliInternational Units per liter |
| 36 | fmol/mL | Femtomoles per milliliter |
| 37 | pmol/mL | Picomoles per milliliter |
| 38 | nmol/mL | Nanomoles per milliliter |
| 39 | umol/mL | Micromoles per milliliter |
| 40 | mol/mL | Moles per milliliter |
| 41 | pmol/dL | Picomoles per deciliter |
| 42 | nmol/dL | Nanomoles per deciliter |
| 43 | umol/dL | Micromoles per deciliter |
| 44 | mmol/dL | Millimoles per deciliter |
| 49 | mol/L | Moles per liter |
| 50 | ueq/mL | MicroEquivalents per milliliter |
| 52 | eq/mL | Equivalents per milliliter |
| 54 | 10⁶ eq/mL | Million Equivalents per milliliter |
| 57 | eq/L | Equivalents per liter |
| 58 | mEq/dL | MilliEquivalents per deciliter |
| 59 | mOsm/L | MilliOsmoles per liter |
| 60 | Osm/L | Osmoles per liter |
| 63 | ng/mL RBCs | Nanograms per milliliter Red Blood Cells |
| 65 | mg/mL | Milligrams per milliliter |
| 67 | pg/dL | Picograms per deciliter |
| 68 | ng/dL | Nanograms per deciliter |
| 70 | ug/dL RBCs | Micrograms per deciliter Red Blood Cells |
| 74 | ng/L | Nanograms per liter |
| 75 | pg/L | Picograms per liter |
| 77 | mg/L | Milligrams per liter |
| 79 | kg/L | Kilograms per liter |
| 80 | /mm³ | Per microliter |
| 81 | Cells/uL | Cells per microliter |
| 82 | RBC/mm³ | Red Blood Cells per microliter |
| 83 | 10³/uL | Thousands per microliter |
| 84 | 10⁶/uL | Millions per microliter |
| 85 | 10⁹/uL | Billions per microliter |
| 86 | /mL | Per milliliter |
| 88 | Copies/mL | Copies per milliliter |
| 89 | 10³/mL | Thousands per milliliter |
| 90 | 10³ copies/mL | Thousand Copies per milliliter |
| 91 | 10⁶/mL | Millions per milliliter |
| 92 | 10⁹/mL | Billions per milliliter |
| 93 | /dL | Per deciliter |
| 94 | /L | Per liter |
| 95 | 10³/L | Thousands per liter |
| 96 | 10⁶/L | Millions per liter |
| 97 | 10¹²/L | Trillions per liter |
| 98 | 10⁹/L | Billions per liter |
| 99 | kUA/L | Allergen Units per liter |
| 100 | RU/L | Relative Units per liter |
| 101 | kIU/L | KiloInternational Units per liter |
| 102 | arb.U/mL | Arbitrary Units per milliliter |

### Interpretations (`codeInterpretation` / `refInterpretation` fields)

These numeric codes indicate the clinical interpretation of a result or reference range.

| Code | Interpretation (EN) | Interpretation (IT) |
|------|---------------------|---------------------|
| 2 | mild | lieve |
| 3 | moderate | moderato |
| 4 | pronounced | pronunciato |
| 5 | severe | grave |
| 6 | significant | significativo |
| 9 | few | poco |
| 11 | large amount | grande quantità |
| 12 | numerous | numeroso |
| 13 | not detected | non rilevato |
| 14 | detected | rilevato |
| 15 | negative | negativo |
| 16 | positive | positivo |
| 22 | traces | tracce |
| 43 | insufficient | insufficiente |
| 44 | deficient | carente |
| 45 | optimal | ottimale |
| 46 | excess | eccesso |
| 47 | critical low | criticamente basso |
| 48 | very high | molto alto |
| 49 | critical high | criticamente alto |
| 50 | sufficient | sufficiente |

---

## Support

For questions, key rotation, or new device registration, contact:

**Alexander** — sasha@dx365.io
