House of Wellness — Supplier Handover Guide
Last updated: 10 April 2026
This document is intended for suppliers and technical partners who need to understand how the HOW Integration Platform interacts with their systems, where data resides, and how it flows through the platform.
The HOW Integration Platform is a private web application used by House of Wellness UK staff to manage blood-test workflows. It integrates three external supplier systems:
| Supplier |
Role |
Integration Type |
| Randox Health |
Laboratory — blood test orders, results, PDFs |
REST API (APIM gateway) |
| OptimalDX |
Report interpretation — functional health reports |
REST API (apiKey auth) |
| GoHighLevel (LeadConnector) |
CRM — patient records, email/SMS communications |
REST API (OAuth2) |
Production URL: https://results.houseofwellnessuk.com (private, Entra ID authenticated)
2. System Architecture Overview
graph TB
subgraph "House of Wellness"
PORTAL["Staff Portal<br/>(Azure Static Web App)"]
API["Integration API<br/>(Azure Functions, .NET 10)"]
STORAGE["Azure Storage<br/>(Table + Blob)"]
KV["Azure Key Vault<br/>(Credentials)"]
end
subgraph "Randox Health"
RAPI["Randox API<br/>(APIM Gateway)"]
end
subgraph "OptimalDX"
OAPI["OptimalDX API<br/>(REST v1)"]
end
subgraph "GoHighLevel"
GAPI["LeadConnector API<br/>(REST)"]
end
PORTAL -->|"HTTPS /api/*"| API
API --> STORAGE
API --> KV
API <-->|"Orders & Results"| RAPI
API <-->|"Patients & Reports"| OAPI
API <-->|"Contacts & Comms"| GAPI
3. Data Residency
All data at rest is stored in Azure UK South (West Europe for the static web app front-end only):
| Data Type |
Storage Location |
Region |
Encryption |
| Case workflow state |
Azure Table Storage |
UK South |
AES-256 at rest |
| Raw Randox results (JSON) |
Azure Blob Storage |
UK South |
AES-256 at rest |
| HL7 messages to OptimalDX |
Azure Blob Storage |
UK South |
AES-256 at rest |
| Report PDFs (ODX + Randox) |
Azure Blob Storage |
UK South |
AES-256 at rest |
| Test Order Form PDFs |
Azure Blob Storage |
UK South |
AES-256 at rest (temporary, deleted on completion) |
| Clinical notes |
Azure Table Storage |
UK South |
AES-256 at rest |
| API credentials & secrets |
Azure Key Vault |
UK South |
HSM-backed |
| Portal static assets |
Azure Static Web App |
West Europe |
AES-256 at rest |
Blob Storage Layout
artifacts/
├── randox/
│ ├── raw/{caseId}.json ← Raw Randox results
│ ├── pdf/{caseId}/{file}.pdf ← Randox report PDFs
│ └── tof/{caseId}.pdf ← Test Order Forms (temporary)
└── odx/
├── hl7/{caseId}.hl7 ← HL7 messages sent to OptimalDX
└── pdf/{caseId}/{file}.pdf ← OptimalDX report PDFs
4. Data Flow — Randox
What we send TO Randox
| Data |
When |
Purpose |
| Patient demographics (name, DOB, biological sex, ethnicity) |
Order creation |
Required for blood test order |
| Panel and test selections |
Order creation |
Which tests to run |
| Sample collection details (date, lab, tubes) |
Order submission |
Lab processing metadata |
External order number (FML001-XXXXXXXX) |
Order creation |
Our tracking reference |
What we receive FROM Randox
| Data |
When |
Purpose |
| Randox order ID + order number |
Order creation response |
Stored for tracking |
| Order status (submitted/pending/completed) |
Hourly polling |
Workflow progression |
| Results detail (analytes, values, units, reference ranges) |
On completion |
Stored as raw JSON, forwarded to OptimalDX |
| Report PDFs (microbiome, NGS, health check panels) |
On completion |
Stored and emailed to patient |
Data Flow Diagram
sequenceDiagram
participant HOW as HOW Platform
participant R as Randox API
Note over HOW,R: Order Lifecycle
HOW->>R: POST /auth/token (credentials)
R-->>HOW: Bearer token
HOW->>R: POST CreatePendingOrder<br/>(patient, panels, tests, externalNumber)
R-->>HOW: { orderId, orderNumber }
HOW->>R: POST UpdateOrder<br/>(sample collection details)
R-->>HOW: OK
loop Hourly polling (status 2/3)
HOW->>R: POST GetOrderStatus<br/>(orderId/orderNumber)
R-->>HOW: { statusId, statusDescription }
end
Note over HOW,R: On Completion (status ≥ 4)
HOW->>R: POST GetOrderResultDetail<br/>(orderId/orderNumber)
R-->>HOW: { reportResults[ analyte, result, units, refRange ] }
opt PDF-only panels
HOW->>R: POST GetOrderResultReports
R-->>HOW: { base64 PDF }
end
Randox Data Retained
| Item |
Retention |
Location |
| Raw results JSON |
Indefinite |
Blob: randox/raw/{caseId}.json |
| Report PDFs |
Indefinite |
Blob: randox/pdf/{caseId}/*.pdf |
| Order references (ID, number, external number) |
Indefinite |
Table: Cases entity fields |
| Test Order Form |
Deleted on completion |
Blob: randox/tof/{caseId}.pdf |
5. Data Flow — OptimalDX
What we send TO OptimalDX
| Data |
When |
Purpose |
| Patient identity (name, DOB, gender, email) |
Patient sync |
Create/match patient in ODX |
| External patient ID (= CRM contact ID) |
Patient sync |
Cross-system linking |
| Blood test results (HL7 ORU^R01 format) |
After Randox results pulled |
Results interpretation |
| Report generation request (practice, patient, test IDs) |
After results submitted |
Generate Functional Health Report PDF |
What we receive FROM OptimalDX
| Data |
When |
Purpose |
| Patient ID |
Patient sync response |
Stored for subsequent calls |
| Patient test ID |
Results submission response |
Used for report generation |
| Functional Health Report PDF |
Report generation response |
Stored and emailed to patient |
| Available report types |
On demand |
UI report selection |
Data Flow Diagram
sequenceDiagram
participant HOW as HOW Platform
participant ODX as OptimalDX API
Note over HOW,ODX: Patient & Results
HOW->>ODX: POST practice/{id}/patient<br/>(name, DOB, gender, email)
ODX-->>HOW: { patientId }
HOW->>ODX: POST patient/{id}/partner/{externalId}<br/>(link CRM contact ID)
ODX-->>HOW: OK
HOW->>ODX: POST patient/{id}/test<br/>(HL7 ORU^R01 message, labProfileId)
ODX-->>HOW: { patientTestId }
Note over HOW,ODX: Report Generation
HOW->>ODX: GET reports/{group}/practice/{id}<br/>(list available reports)
ODX-->>HOW: [ { reportId, name, ... } ]
HOW->>ODX: POST reports/FunctionalHealthReport<br/>(practiceId, patientId, patientTestId, reportIds[])
ODX-->>HOW: PDF bytes
Results are submitted as HL7 v2.3 ORU^R01 messages:
MSH|^~\&|HOW|HOW|OptimalDX|OptimalDX|...||ORU^R01|...|P|2.3
PID|||{patientId}^^^MR||...
OBR|1|{orderNumber}||RANDOX^Blood Test Results^L|||...
OBX|1|NM|{code}^{name}^99RDX||{value}|{units}|{refLow}-{refHigh}|{flag}|||F
OBX|2|NM|...
Each analyte from Randox is mapped to an OBX segment with code system 99RDX.
OptimalDX Data Retained
| Item |
Retention |
Location |
| HL7 messages |
Indefinite |
Blob: odx/hl7/{caseId}.hl7 |
| Report PDFs |
Indefinite |
Blob: odx/pdf/{caseId}/*.pdf |
| Patient/test IDs |
Indefinite |
Table: Cases entity fields |
6. Data Flow — GoHighLevel (CRM)
What we read FROM CRM
| Data |
When |
Purpose |
| Contact details (name, email, phone, DOB, biological sex) |
Case creation, order creation |
Patient identity |
| Custom field values (DOB, biological sex, athlete) |
Order creation |
Randox/ODX requirements |
What we write TO CRM
| Data |
When |
Purpose |
| New contacts |
Patient creation from portal |
CRM is system of record |
| Updated contact fields |
Portal edits (DOB, sex, etc.) |
Keep CRM current |
| PDF attachments |
After report generation |
Conversation attachments |
| Emails (with PDF) |
After report generation |
Patient notification |
| SMS messages |
After report generation |
Patient notification |
CRM Data Flow Diagram
sequenceDiagram
participant Staff as Portal Staff
participant HOW as HOW Platform
participant CRM as GoHighLevel
Staff->>HOW: Search for patient
HOW->>CRM: GET contacts/?query=...
CRM-->>HOW: Contact list
HOW-->>Staff: Display results
Staff->>HOW: Create/update patient
HOW->>CRM: POST/PUT contacts/
CRM-->>HOW: { contactId }
Note over HOW,CRM: After Report Generated
HOW->>CRM: POST conversations/messages/upload<br/>(PDF file)
CRM-->>HOW: { attachmentUrl }
HOW->>CRM: POST conversations/messages<br/>(type=Email, attachments, subject, html)
CRM-->>HOW: { messageId }
HOW->>CRM: POST conversations/messages<br/>(type=SMS, message)
CRM-->>HOW: { messageId }
CRM Data Retained Locally
| Item |
Retention |
Location |
| CRM contact ID (reference only) |
Indefinite |
Table: Cases.CrmContactId |
| CRM location ID (reference only) |
Indefinite |
Table: Cases.CrmLocationId |
| Patient name (cached display) |
Indefinite |
Table: Cases.PatientName |
| Clinical notes |
Indefinite |
Table: ContactNotes |
Note: Patient PII (full demographics) is NOT permanently stored in our system. The CRM contact ID is stored as a reference. Full patient details are fetched from CRM on demand.
7. Security Summary
| Control |
Implementation |
| Transport |
HTTPS only (TLS 1.2+) on all endpoints |
| User authentication |
Entra ID (Azure AD) via SWA built-in auth |
| Role-based access |
How_Admin, How_Staff, How_ReadOnly roles enforced server-side |
| Credential storage |
Azure Key Vault (HSM-backed), accessed via Managed Identity |
| No connection strings |
All Azure resources accessed via DefaultAzureCredential |
| Secrets never logged |
Diagnostic endpoints return metadata only, never secret values |
| Data at rest |
AES-256 encryption (Azure Storage + Key Vault) |
| API resilience |
All HTTP clients use retry + circuit breaker + timeout (Microsoft.Extensions.Http.Resilience) |
| CI/CD |
GitHub Actions with OIDC federation (no stored credentials) |
8. Environment Summary
| Resource |
Production |
Dev (stopped) |
| Subscription |
prod-spoke-uks |
dev-spoke-uks |
| Resource Group |
how-prod-integration-rg |
how-poc-integration-rg |
| Function App |
how-prod-api-func |
how-poc-api-func ⏸️ |
| Static Web App |
how-prod-portal |
how-poc-portal |
| Docs Site |
how-prod-docs |
— |
| Storage Account |
howprodintegration |
howpocintegrationrg9f7a |
| Key Vault |
how-prod-kv |
how-poc-kv |
| Custom Domain |
results.houseofwellnessuk.com |
— |
| Docs Domain |
docs.houseofwellnessuk.com |
— |
| Framework |
.NET 10 (LTS) |
— |
| Region (compute) |
UK South |
UK South |
| Region (SWA) |
West Europe |
West Europe |
| Role |
Contact |
| Technical Lead |
Devtech Cloud (josh@devtechcloud.com) |
| Business Owner |
House of Wellness UK |