# Bloodwork Tracker API Documentation

Base URL:

```text
https://health-tracker.project4.net/api
```

The API has two authentication modes:

- Browser app endpoints use the secure `bwt_session` cookie created by passwordless login.
- External API endpoints use API keys created on the app's **API** page.

Use external endpoints for integrations.

## External API Authentication

Create an API key in the app:

1. Sign in as an admin.
2. Open **API**.
3. Enter a label.
4. Select **Create key**.
5. Copy the key immediately. It is shown once.

Send the key using either header:

```http
X-API-Key: bwt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

or:

```http
Authorization: Bearer bwt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

## Content Type

For write requests:

```http
Content-Type: application/json
```

All responses are JSON.

## Success And Error Codes

Every API response includes a stable machine-readable response code in the `X-BWT-Code` header.

Object responses also include a top-level `code` field. Array responses keep their existing array body for backward compatibility, so read `X-BWT-Code` for those endpoints.

Common success codes:

- `BWT_SUCCESS`: successful read or update
- `BWT_CREATED`: resource created
- `BWT_OK`: successful boolean-style operation, such as delete or logout

## Errors

Error response:

```json
{
  "code": "BWT_API_KEY_INVALID",
  "message": "Invalid or missing API key"
}
```

Common status codes:

- `200`: success
- `201`: created
- `400`: invalid input
- `401`: missing or invalid API key, or missing browser session
- `403`: forbidden
- `404`: not found
- `500`: server error

Error codes:

- `BWT_INVALID_JSON`: request body could not be parsed as JSON
- `BWT_VALIDATION_FAILED`: required fields or values are missing or invalid
- `BWT_INVALID_DATE`: date is not valid `YYYY-MM-DD`
- `BWT_INVALID_NUMBER`: number field is not numeric
- `BWT_INVALID_OTP`: one-time login code is invalid
- `BWT_API_KEY_INVALID`: API key is missing, inactive, revoked, or incorrect
- `BWT_UNAUTHORIZED`: browser session is missing or invalid
- `BWT_FORBIDDEN`: authenticated user cannot perform the action
- `BWT_NOT_FOUND`: route or generic resource not found
- `BWT_TEST_NOT_FOUND`: blood test not found for this user/key
- `BWT_RESULT_NOT_FOUND`: result not found for this user/key
- `BWT_CONFLICT`: request conflicts with current state
- `BWT_RATE_LIMITED`: too many requests
- `BWT_SERVER_ERROR`: unexpected server error

## Data Types

### Blood Test

```json
{
  "id": 123,
  "testName": "Haemoglobin",
  "groupName": "GENERAL HAEMATOLOGY",
  "unitOfMeasure": "g/L",
  "refRangeLow": 135,
  "refRangeHigh": 180,
  "createdAt": "2026-05-06 10:00:00+00"
}
```

`refRangeLow` and `refRangeHigh` may be `null`.

### Result

```json
{
  "id": 456,
  "testId": 123,
  "testDate": "2026-05-06",
  "value": 151,
  "createdAt": "2026-05-06 10:00:00+00"
}
```

Dates use `YYYY-MM-DD`.

## External Endpoints

### Get Complete Dataset

```http
GET /external/bootstrap
```

Response:

```json
{
  "code": "BWT_SUCCESS",
  "tests": [],
  "results": [],
  "clinicalNotes": "Patient context..."
}
```

Example:

```bash
curl https://health-tracker.project4.net/api/external/bootstrap \
  -H "X-API-Key: $BWT_API_KEY"
```

### List Tests

```http
GET /external/tests
```

Example:

```bash
curl https://health-tracker.project4.net/api/external/tests \
  -H "Authorization: Bearer $BWT_API_KEY"
```

Response:

```json
[
  {
    "id": 123,
    "testName": "Haemoglobin",
    "groupName": "GENERAL HAEMATOLOGY",
    "unitOfMeasure": "g/L",
    "refRangeLow": 135,
    "refRangeHigh": 180,
    "createdAt": "2026-05-06 10:00:00+00"
  }
]
```

### Get Test With Results

```http
GET /external/tests/{id}
```

Example:

```bash
curl https://health-tracker.project4.net/api/external/tests/123 \
  -H "Authorization: Bearer $BWT_API_KEY"
```

Response:

```json
{
  "code": "BWT_SUCCESS",
  "test": {
    "id": 123,
    "testName": "Haemoglobin",
    "groupName": "GENERAL HAEMATOLOGY",
    "unitOfMeasure": "g/L",
    "refRangeLow": 135,
    "refRangeHigh": 180,
    "createdAt": "2026-05-06 10:00:00+00"
  },
  "results": []
}
```

### Create Test

```http
POST /external/tests
```

Request:

```json
{
  "testName": "Ferritin",
  "groupName": "IRON STUDIES",
  "unitOfMeasure": "ug/L",
  "refRangeLow": 30,
  "refRangeHigh": 400
}
```

Response status: `201`

Response:

```json
{
  "code": "BWT_CREATED",
  "id": 124,
  "testName": "Ferritin",
  "groupName": "IRON STUDIES",
  "unitOfMeasure": "ug/L",
  "refRangeLow": 30,
  "refRangeHigh": 400,
  "createdAt": "2026-05-06 10:00:00+00"
}
```

Example:

```bash
curl -X POST https://health-tracker.project4.net/api/external/tests \
  -H "Authorization: Bearer $BWT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "testName": "Ferritin",
    "groupName": "IRON STUDIES",
    "unitOfMeasure": "ug/L",
    "refRangeLow": 30,
    "refRangeHigh": 400
  }'
```

### Update Test

```http
PUT /external/tests/{id}
PATCH /external/tests/{id}
```

Request:

```json
{
  "testName": "Ferritin",
  "groupName": "IRON STUDIES",
  "unitOfMeasure": "ug/L",
  "refRangeLow": 30,
  "refRangeHigh": 400
}
```

Response:

```json
{
  "code": "BWT_SUCCESS",
  "id": 123,
  "testName": "Ferritin",
  "groupName": "IRON STUDIES",
  "unitOfMeasure": "ug/L",
  "refRangeLow": 30,
  "refRangeHigh": 400,
  "createdAt": "2026-05-06 10:00:00+00"
}
```

PUT example:

```bash
curl -X PUT https://health-tracker.project4.net/api/external/tests/123 \
  -H "Authorization: Bearer $BWT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "testName": "Ferritin",
    "groupName": "IRON STUDIES",
    "unitOfMeasure": "ug/L",
    "refRangeLow": 30,
    "refRangeHigh": 400
  }'
```

PATCH example:

```bash
curl -X PATCH https://health-tracker.project4.net/api/external/tests/123 \
  -H "Authorization: Bearer $BWT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "testName": "Ferritin",
    "groupName": "IRON STUDIES",
    "unitOfMeasure": "ug/L",
    "refRangeLow": 30,
    "refRangeHigh": 400
  }'
```

### Delete Test

```http
DELETE /external/tests/{id}
```

Deleting a test also deletes its results.

Response:

```json
{
  "code": "BWT_OK",
  "ok": true
}
```

Example:

```bash
curl -X DELETE https://health-tracker.project4.net/api/external/tests/123 \
  -H "Authorization: Bearer $BWT_API_KEY"
```

### List Results

```http
GET /external/results
```

Optional query parameters:

- `testId`: return only one test's results
- `from`: include results on or after this date
- `to`: include results on or before this date

Example:

```bash
curl "https://health-tracker.project4.net/api/external/results?testId=123&from=2026-01-01&to=2026-12-31" \
  -H "Authorization: Bearer $BWT_API_KEY"
```

Response:

```json
[
  {
    "id": 456,
    "testId": 123,
    "testDate": "2026-05-06",
    "value": 151,
    "createdAt": "2026-05-06 10:00:00+00"
  }
]
```

### Get Result

```http
GET /external/results/{id}
```

Response:

```json
{
  "code": "BWT_SUCCESS",
  "id": 456,
  "testId": 123,
  "testDate": "2026-05-06",
  "value": 151,
  "createdAt": "2026-05-06 10:00:00+00"
}
```

Example:

```bash
curl https://health-tracker.project4.net/api/external/results/456 \
  -H "Authorization: Bearer $BWT_API_KEY"
```

### Create Result

```http
POST /external/results
```

Request:

```json
{
  "testId": 123,
  "testDate": "2026-05-06",
  "value": 151
}
```

Response status: `201`

Response:

```json
{
  "code": "BWT_CREATED",
  "id": 456,
  "testId": 123,
  "testDate": "2026-05-06",
  "value": 151,
  "createdAt": "2026-05-06 10:00:00+00"
}
```

If a result already exists for the same `testId` and `testDate`, the value is updated and the existing result is returned.

Example:

```bash
curl -X POST https://health-tracker.project4.net/api/external/results \
  -H "Authorization: Bearer $BWT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "testId": 123,
    "testDate": "2026-05-06",
    "value": 151
  }'
```

### Update Result

```http
PUT /external/results/{id}
PATCH /external/results/{id}
```

Request:

```json
{
  "testId": 123,
  "testDate": "2026-05-06",
  "value": 152
}
```

Response:

```json
{
  "code": "BWT_SUCCESS",
  "id": 456,
  "testId": 123,
  "testDate": "2026-05-06",
  "value": 152,
  "createdAt": "2026-05-06 10:00:00+00"
}
```

PUT example:

```bash
curl -X PUT https://health-tracker.project4.net/api/external/results/456 \
  -H "Authorization: Bearer $BWT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "testId": 123,
    "testDate": "2026-05-06",
    "value": 152
  }'
```

PATCH example:

```bash
curl -X PATCH https://health-tracker.project4.net/api/external/results/456 \
  -H "Authorization: Bearer $BWT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "testId": 123,
    "testDate": "2026-05-06",
    "value": 152
  }'
```

### Delete Result

```http
DELETE /external/results/{id}
```

Response:

```json
{
  "code": "BWT_OK",
  "ok": true
}
```

Example:

```bash
curl -X DELETE https://health-tracker.project4.net/api/external/results/456 \
  -H "Authorization: Bearer $BWT_API_KEY"
```

### Get Clinical Notes

```http
GET /external/clinical-notes
```

Response:

```json
{
  "code": "BWT_SUCCESS",
  "clinicalNotes": "Patient context..."
}
```

Example:

```bash
curl https://health-tracker.project4.net/api/external/clinical-notes \
  -H "Authorization: Bearer $BWT_API_KEY"
```

### Update Clinical Notes

```http
PUT /external/clinical-notes
```

Request:

```json
{
  "clinicalNotes": "Updated patient context..."
}
```

Response:

```json
{
  "code": "BWT_SUCCESS",
  "clinicalNotes": "Updated patient context..."
}
```

Example:

```bash
curl -X PUT https://health-tracker.project4.net/api/external/clinical-notes \
  -H "Authorization: Bearer $BWT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "clinicalNotes": "Updated patient context..."
  }'
```

## Browser App Endpoints

These endpoints are used by the web app and require a valid session cookie.

### Authentication

```http
POST /auth/send-code
POST /auth/verify-code
POST /auth/logout
GET /auth/user
GET /access
```

### App Data

```http
GET /bootstrap
POST /tests
DELETE /tests/{id}
POST /results
DELETE /results/{id}
POST /settings
POST /clinical-notes
POST /tests/merge
POST /import
DELETE /clear-all
```

### Admin

```http
POST /invitations
DELETE /invitations/{id}
POST /api-keys
DELETE /api-keys/{id}
```

## Example Integration

```bash
export BWT_API_KEY="bwt_..."

curl https://health-tracker.project4.net/api/external/tests \
  -H "Authorization: Bearer $BWT_API_KEY"

curl https://health-tracker.project4.net/api/external/results \
  -H "Authorization: Bearer $BWT_API_KEY"
```

Create a new result:

```bash
curl -X POST https://health-tracker.project4.net/api/external/results \
  -H "Authorization: Bearer $BWT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"testId":123,"testDate":"2026-05-06","value":151}'
```

## Security Notes

- API keys grant access to the issuing user's bloodwork data.
- Store API keys like passwords.
- Revoke unused keys from the app's API page.
- The API never returns full API keys after creation.
- Browser login codes are single-use, hashed in storage, and expire after 10 minutes.
