Data model
Every canonical record, field and quantity name, with its unit, UCUM and LOINC codes.
This file is generated by go run ./internal/model/cmd/gendocs from
internal/model. Do not edit it by hand: a test fails when it differs from
the generator output.
Every connector maps what a provider sends into the records below. The ETL then
converts units, validates, de-duplicates and writes them to the ohs_data
schema. Every stored row carries tenant_id and user_id; the two are
set by the pipeline and are never part of a content hash. Times are UTC. A field
typed as a pointer is optional and reads as null in the API when absent.
Field names below are the Go names used inside the pipeline, not the wire names:
the JSON the API returns uses snake_case names, and some differ (for example
SummaryDate is date). Use ../api/openapi.yaml for the JSON shapes.
The read API returns these records with the provenance fields provider,
source, device and ingested_at; see api.md. ECG
recordings, ECG voltage samples and heartbeat (RR interval) samples are stored but
have no read route yet: they are planned.
Canonical types
QuantitySample
QuantitySample represents a single scalar health measurement.
Stored in ohs_data.quantity_samples.
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
UserID | string | Owning end user. Set by the pipeline. |
Provider | string | The integration the sample came through (apple_health, withings). |
SourceSlug | string | The vendor that produced the sample. Equals Provider for a direct integration. |
SourceName | string | |
SourceBundleID | string | |
DeviceID | string | |
ProductType | string | Exact hardware model that ran the writing app, for example Watch7,2. |
DeviceModel | string | Model of the device that took the reading. |
WasUserEntered | *bool | Null when the source does not say. |
ExternalID | string | |
QuantityType | string | Canonical name from the table below, for example heart_rate. |
Value | float64 | |
Unit | string | Storage unit of the quantity name, after conversion. |
IsPrimarySource | bool | |
StartDate | time.Time | |
EndDate | time.Time | |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
Metadata | map[string]any | Provider fields kept as sent, plus timezone (IANA name) when the provider gives one. |
Replaceable | bool | Writer instruction, not a column: a later value for a still open window replaces the stored one. |
SupersedeMoved | bool | Writer instruction, not a column: the external id names one figure of one record, so a stored copy under another start is deleted. |
CategorySample
CategorySample represents a categorical health event.
Stored in ohs_data.category_samples.
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
UserID | string | Owning end user. Set by the pipeline. |
Provider | string | |
ExternalID | string | |
CategoryType | string | |
ValueInt | int | |
ValueLabel | string | |
StartDate | time.Time | |
EndDate | time.Time | |
SourceName | string | |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
Metadata | map[string]any | Provider fields kept as sent, plus timezone (IANA name) when the provider gives one. |
SleepSession
SleepSession represents a structured sleep record with duration breakdowns.
Stored in ohs_data.sleep_sessions.
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
UserID | string | Owning end user. Set by the pipeline. |
Provider | string | |
ExternalID | string | |
BedtimeStart | time.Time | |
BedtimeEnd | time.Time | |
TotalSleepSeconds | *int | |
DeepSleepSeconds | *int | |
REMSleepSeconds | *int | |
LightSleepSeconds | *int | |
AwakeSeconds | *int | |
SleepScore | *float64 | |
HRVAvgMs | *float64 | |
RestingHR | *float64 | |
RespiratoryRate | *float64 | |
SpO2Avg | *float64 | |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
Metadata | map[string]any | Provider fields kept as sent, plus timezone (IANA name) when the provider gives one. |
Replaceable | bool | Writer instruction, not a column: a re-scored session replaces the stored figures and phases. |
SleepPhase
SleepPhase represents a single phase within a sleep session.
Stored in ohs_data.sleep_phases.
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
UserID | string | Owning end user. Set by the pipeline. |
Phase | string | deep, rem, light, awake, in_bed or asleep_core, after label normalization. |
StartDate | time.Time | |
EndDate | time.Time | |
DurationS | int | |
ParentContentHash | string | Content hash of the parent session. Empty for an orphan phase. |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
Workout
Workout represents a workout session with performance metrics.
Stored in ohs_data.workouts.
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
UserID | string | Owning end user. Set by the pipeline. |
Provider | string | |
ExternalID | string | |
ActivityType | string | |
StartDate | time.Time | |
EndDate | time.Time | |
DurationS | *int | |
ActiveEnergyKcal | *float64 | |
DistanceM | *float64 | |
AvgHeartRate | *float64 | |
MaxHeartRate | *float64 | |
AvgSpeedMps | *float64 | |
ElevationGainM | *float64 | |
VO2Max | *float64 | |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
Metadata | map[string]any | Provider fields kept as sent, plus timezone (IANA name) when the provider gives one. |
Replaceable | bool | Writer instruction, not a column: an edited workout replaces the stored figures. |
RoutePoint
RoutePoint represents a GPS coordinate from a workout route.
Stored in ohs_data.route_points.
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
UserID | string | Owning end user. Set by the pipeline. |
Timestamp | time.Time | |
Latitude | float64 | |
Longitude | float64 | |
AltitudeM | *float64 | |
SpeedMps | *float64 | |
CourseDeg | *float64 | |
ParentContentHash | string | Content hash of the parent workout. Empty for an orphan point. |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
NutritionLog
NutritionLog represents a nutrition/dietary record.
Stored in ohs_data.nutrition_logs.
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
UserID | string | Owning end user. Set by the pipeline. |
Provider | string | |
ExternalID | string | |
StartDate | time.Time | |
EndDate | *time.Time | |
MealType | string | |
EnergyKcal | *float64 | |
ProteinG | *float64 | |
CarbsG | *float64 | |
FatG | *float64 | |
FiberG | *float64 | |
SugarG | *float64 | |
SodiumMg | *float64 | |
NutrientsJSON | map[string]any | Extended nutrients by name. |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
Metadata | map[string]any | Provider fields kept as sent, plus timezone (IANA name) when the provider gives one. |
Replaceable | bool | Writer instruction, not a column. |
ECGRecording
ECGRecording represents ECG metadata (voltage data is in ECGVoltageSample).
Stored in ohs_data.ecg_recordings. No read route yet (planned): stored, not served by the API or MCP.
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
UserID | string | Owning end user. Set by the pipeline. |
StartDate | time.Time | |
EndDate | time.Time | |
Classification | int | |
SymptomsStatus | int | |
AverageHeartRate | *float64 | |
SamplingFrequency | float64 | |
NumberOfMeasurements | int | |
VoltagePath | string | |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
Metadata | map[string]any | Provider fields kept as sent, plus timezone (IANA name) when the provider gives one. |
ECGVoltageSample
ECGVoltageSample represents a single voltage measurement from an ECG recording.
Stored in ohs_data.ecg_samples. No read route yet (planned): stored, not served by the API or MCP.
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
ECGID | string | |
UserID | string | Owning end user. Set by the pipeline. |
TimeSinceSampleStart | float64 | |
VoltageUV | float64 | Microvolts, converted from volts. |
ParentContentHash | string | Content hash of the parent recording. Empty for an orphan sample. |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
DailySummary
DailySummary represents an aggregated daily score from a provider.
Stored in ohs_data.daily_summaries.
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
UserID | string | Owning end user. Set by the pipeline. |
Provider | string | |
SummaryDate | time.Time | The user's local calendar day. |
ReadinessScore | *float64 | |
ActivityScore | *float64 | |
SleepScore | *float64 | |
RecoveryScore | *float64 | |
StrainScore | *float64 | |
StepCount | *int | |
ActiveKcal | *float64 | |
TotalKcal | *float64 | |
DistanceM | *float64 | |
FloorsClimbed | *int | |
RestingHR | *float64 | |
HRVAvgMs | *float64 | |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
Metadata | map[string]any | Provider fields kept as sent, plus timezone (IANA name) when the provider gives one. |
Replaceable | bool | Writer instruction, not a column: the latest summary for the day wins. |
RecordID | string | Writer instruction, not a column: the provider record whose figures the summary carries; not written once that record is deleted. |
HeartbeatSample
HeartbeatSample represents a single beat-to-beat RR interval measurement.
Stored in ohs_data.heartbeat_samples.
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
SeriesID | string | |
UserID | string | Owning end user. Set by the pipeline. |
Timestamp | time.Time | |
RRMS | float64 | RR interval in milliseconds. |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
Observation
Observation is a generic coded health fact (the long-tail catch-all).
Stored in ohs_data.observations. No connector writes it yet, and no read route serves it (planned).
| Field | Type | Note |
|---|---|---|
TenantID | string | Owning tenant. Set by the pipeline. |
UserID | string | Owning end user. Set by the pipeline. |
TS | time.Time | |
EndTS | time.Time | |
CanonicalCode | string | Controlled code, not free text. |
ValueNum | *float64 | |
ValueCode | string | |
ValueText | string | |
UnitUCUM | string | UCUM unit of the value. |
ValueCanonical | *float64 | |
Provider | string | |
SourceName | string | |
DeviceID | string | |
IsPrimarySource | bool | |
TypeConcept | string | device, survey, ehr or derived. |
SourceValue | string | |
S3Ref | string | |
ContentHash | string | De-duplication identity of the source record. Part of the unique key with the time column. |
Metadata | map[string]any | Provider fields kept as sent, plus timezone (IANA name) when the provider gives one. |
MapResult
MapResult holds all canonical records produced from a single input record.
| Field | Type | Note |
|---|---|---|
QuantitySamples | []QuantitySample | |
CategorySamples | []CategorySample | |
SleepSessions | []SleepSession | |
SleepPhases | []SleepPhase | |
Workouts | []Workout | |
RoutePoints | []RoutePoint | |
NutritionLogs | []NutritionLog | |
ECGRecordings | []ECGRecording | |
ECGSamples | []ECGVoltageSample | |
HeartbeatSamples | []HeartbeatSample | |
DailySummaries | []DailySummary | |
Observations | []Observation | |
HighFrequency | bool | The result holds high volume data and is written with COPY. |
SourceKey | string | Raw object the batch was mapped from. Child records use it to find a parent that arrived in another object. |
Deletions | []Deletion | Provider records the provider deleted: their stored rows are removed and a tombstone in ohs_data.deleted_records keeps them from being written again. |
Quantity names and storage units
A quantity sample is stored in the unit below. The ETL converts a known source unit into it (pounds to kilograms, Fahrenheit to Celsius, mmol/L to mg/dL and so on) and parks a sample whose unit it cannot convert. A quantity name that is not in this table is stored as the provider sent it, unit included. Percentages are stored as 0 to 100, not as fractions.
| Quantity name | Storage unit |
|---|---|
active_energy_burned | kcal |
active_energy_burned_intraday | kcal |
apple_exercise_time | min |
apple_move_time | min |
apple_sleeping_wrist_temperature | celsius |
apple_stand_time | min |
basal_body_temperature | celsius |
basal_energy_burned | kcal |
blood_alcohol_content | percent |
blood_glucose | mg/dL |
blood_pressure_diastolic | mmHg |
blood_pressure_systolic | mmHg |
body_fat_mass | kg |
body_fat_percentage | percent |
body_mass | kg |
body_mass_index | kg/m2 |
body_temperature | celsius |
body_temperature_deviation | celsius |
bone_mass | kg |
cycling_power | W |
distance_cycling | m |
distance_downhill_snow_sports | m |
distance_swimming | m |
distance_walking_running | m |
distance_walking_running_intraday | m |
distance_wheelchair | m |
electrodermal_activity | uS |
environmental_audio_exposure | dBASPL |
environmental_sound_reduction | dBASPL |
flights_climbed | count |
forced_expiratory_volume_1 | L |
forced_vital_capacity | L |
headphone_audio_exposure | dBASPL |
heart_rate | bpm |
height | cm |
hrv_rmssd | ms |
hrv_sdnn | ms |
inhaler_usage | count |
insulin_delivery | IU |
lean_body_mass | kg |
muscle_mass | kg |
nike_fuel | count |
number_of_alcoholic_beverages | count |
number_of_times_fallen | count |
oxygen_saturation | percent |
peak_expiratory_flow_rate | L/min |
physical_effort | kcal/kg/h |
pulse_wave_velocity | m/s |
push_count | count |
readiness_hrv_balance | index |
readiness_score | score |
recovery_high_time | min |
respiratory_rate | bpm |
resting_heart_rate | bpm |
running_ground_contact_time | ms |
running_power | W |
running_speed | m/s |
running_stride_length | m |
running_vertical_oscillation | cm |
skin_temperature | celsius |
sleep_score | score |
step_count | count |
step_count_intraday | count |
strain | index |
stress_high_time | min |
underwater_depth | m |
uv_exposure | index |
vascular_age | years |
visceral_fat_rating | index |
vo2_max | mL/kg/min |
waist_circumference | cm |
walking_heart_rate_average | bpm |
walking_speed | m/s |
water_mass | kg |
water_temperature | celsius |
workout_strain | index |