Skip to main content
Last updated: 2026-05-25 (aligned with platform baseline 488e91e / tag baseline).

Overview

Health Yourself collects questionnaire answers and blood results in your intake UX (see the Input Data Preparation Guide). Before calling Idunox, your backend maps that data into canonical Type A JSON — a structured subject block, a flat markers[] blood panel, optional sourceMetadata, and options.
Do not send legacy markers.healthQuestionnaireMarkers / markers.bloodMarkers arrays or top-level requestedAssessments. Those shapes are not accepted by the live partner API.
ConceptCanonical location
Questionnaire (36 items)subject.demographics, subject.measurements, subject.history, subject.familyHistory
Blood panel (14 analytes)markers[] with code, value, unit
Lab reference intervalssourceMetadata.markerRanges (structured bounds per marker code)
Intake observation timesourceMetadata.sourceTimestamp (single timestamp for the bundle)
Wellbeing areas to scoreoptions.requestedOutcomes (public wellbeing.* ids)
Report artefactsoptions.requestedOutputs (e.g. json, html, pdf, inference_score_v1)

Top-level fields

FieldRequiredNotes
schemaVersionYesAlways canonical_submission_v1
partnerIdYesUUID of your PartnerCredential (must match the API key used in Authorization; mismatch → 403 PARTNER_ID_MISMATCH)
partnerSubmissionIdYesYour unique submission reference (max 512 chars)
partnerSubjectIdYesDe-identified subject reference — no names, MRNs, email, or addresses
subjectYesdemographics required; HY should also send measurements, history, and familyHistory
markersYes*Non-empty array of 14 blood markers for Health Yourself
optionsYesrequestedOutputs required (min 1); requestedOutcomes optional (max 8 entries)
sourceMetadataRecommendedsourceTimestamp, sourceSystem, labName, facilityId, and markerRanges
questionnaireOptionalFuture wellbeing Likert / B2B checklist only — not the 36 HY markers
clinicalExtensionsOptionalReserved key bag (max 64 keys); still scanned for disallowed identifier field names
reportDocumentsOptionalAlternative to markers when submitting report metadata only
* At least one of markers or reportDocuments must be non-empty. Health Yourself always sends the 14-marker panel in markers.

Numeric sentinel (-1)

Optional integer and number fields on smoking and family history accept -1 as “not applicable” (TYPE_A_NOT_APPLICABLE in the platform). The server treats -1 like a missing value for branch rules (e.g. stopAge is not considered “set” when -1).
  • Prefer omitting a key when the intake branch does not collect that field.
  • Use -1 only when your serializer must send a number but the UI path is “Not applicable”.
  • Do not use -1 on blood markers[].value — values must be finite numbers within range.

Questionnaire → canonical mapping

Map intake labels to canonical enum values (lowercase snake_case). Omit keys that do not apply; do not send null for optional numbers unless your serializer requires it.

Demographics and measurements

Intake (Q ID)Canonical pathCanonical values
Q001 Sex at birthsubject.demographics.sexAtBirthmale, female
Q002 Agesubject.demographics.ageYears or birthYearageYears: integer 0..120; or birthYear: 1900..2100 (at least one required)
Q003 Weightsubject.measurements.weightKgnumber 0..300
Q004 Heightsubject.measurements.heightCmnumber 0..250
Q005 Self-rated healthsubject.history.overallHealthexcellent, very_good, good, fair, poor, unknown (map UI labels to lowercase enums)
Optional demographics: ethnicity (string), educationYears (integer 0..40).

Smoking (subject.history.smoking)

status (required when smoking is sent): never, former, current, prefer_not_to_say pastFrequency / currentFrequency (when allowed): none, light, moderate, heavy, unknown
UKB / intake labelCanonical frequency
I have never smokednone
Just tried once or twicelight
Only occasionallylight
On most or all daysheavy
No (current-frequency auto)none
IntakeCanonical fieldRules
Q006 StatusstatusNever smokernever; Former smokerformer; Current smokercurrent
Q007 Past frequencypastFrequencyOmit when status is never, current, or prefer_not_to_say. For former, map labels per table above.
Q008 Current frequencycurrentFrequencyOnly when status: "current". Must be omitted for former, never, and prefer_not_to_say (do not send "none" on former).
Q009 Current cigs/daycurrentCigsPerDaynumber 0..200 or -1. Only for current; must be omitted for former / never.
Q010 Past cigs/daypastCigsPerDaynumber 0..200 or -1. Omit for current / never.
Q011 Stop agestopAgeinteger 0..120 or -1. Required when status: "former" (use -1 on light/occasional former branches if needed). Omit for current / never.
Q012 Start agestartAgeinteger 0..120 or -1. Heavy current or heavy former branch; otherwise omit.
Current smokers: provide currentCigsPerDay and/or currentFrequency (not unknown) so intensity is defined. Never / prefer not to say: send only status (or omit the whole smoking object for never-only paths); all other smoking keys must be omitted.
"smoking": {
  "status": "former",
  "pastFrequency": "heavy",
  "pastCigsPerDay": 12,
  "startAge": 19,
  "stopAge": 41
}
Do not include currentFrequency or currentCigsPerDay.

Conditions and medications

IntakeCanonical pathMapping
Q013–Q017subject.history.conditions.*Yestrue, Nofalse for hasDiabetes, hasDementia, hasCardioCerebrovascular, hasLungCancer, hasKidneyDisease
Q018 Medicationsubject.history.medications.takesRegularMedicationYestrue, Nofalse. If you send a medications object, takesRegularMedication is required.

Family history (subject.familyHistory)

Each parent (father, mother) uses aliveStatus, optional ages, and conditions[].
IntakeCanonicalMapping
Q019 / Q028 AlivealiveStatusYesalive; Nodeceased; I do not knowunknown
Q020 / Q029 Death agedeathAgeinteger 0..120 or -1. Required when aliveStatus: deceased. Omit or -1 when alive or unknown.
Q021 / Q030 Current agecurrentAgeinteger 0..120 or -1. Required when aliveStatus: alive. Omit or -1 when deceased or unknown.
Q022–Q027 / Q031–Q036conditions[]Each UI Yes adds a code; each No omits that code
UI “Yes” for…conditions[] code
Alzheimer’s / Dementiaalzheimer_disease and/or dementia
Diabetesdiabetes_mellitus
Heart diseasecardiovascular_disease
High blood pressurehypertension
Lung cancerlung_cancer
Strokecerebrovascular_disease
All allowed conditions[] codes: diabetes_mellitus, cardiovascular_disease, cerebrovascular_disease, dementia, alzheimer_disease, kidney_disease, lung_cancer, other_malignancy, hypertension, none_known, unknown.
Align aliveStatus with ages. If father is deceased, send deathAge only (omit currentAge). If mother is alive, send currentAge only (omit deathAge). Swapping these causes validation errors.

Blood markers → markers[]

Use canonical marker codes in markers[].code (not B### guide ids in the JSON body). Submit exactly 14 rows — one per code, no duplicates. Aliases resolve to the same canonical code (e.g. ALBALBUMIN, HBA1CHBA1C_MMOL_MOL with mmol/mol unit).
B# (guide)API codeRequired unitDefault analytical value range*
B001ALBUMINg/L15..60
B002ALTU/L3..500
B003ALPU/L5..1500
B006ASTU/L3..1000
B007CALCIUMmmol/L1..5
B008CHOLmmol/L0.5..18
B010CYSTATIN_Cmg/L0.1..8.99
B012GGTU/L5..1200
B014HDLmmol/L0.05..4.65
B015CRPmg/L0.08..80
B018LDLmmol/L0.26..10.3
B020PHOSPHATEmmol/L0.32..6.4
B028URATEµmol/L89..1785
B030HBA1C_MMOL_MOLmmol/mol15..515.2
*When sourceMetadata.markerRanges.{CODE} is present for a marker, validation uses your lowerBound..upperBound instead of the preset analytical range. µ / μ in units normalize to the same value as u for comparison. Accepted code aliases (non-exhaustive): ALB, CA, TCHOL / TOTAL_CHOLESTEROL, CYSTATIN, HDL_CHOLESTEROL, C_REACTIVE_PROTEIN, LDL_DIRECT, PHOS, UA, HBA1C (with mmol/mol unit only). Each array element:
{ "code": "ALT", "value": 28, "unit": "U/L" }
  • value must be a finite number (JSON number preferred).
  • Optional per-marker observedAt (ISO-8601); otherwise use sourceMetadata.sourceTimestamp for the panel.

Lab reference ranges (sourceMetadata.markerRanges)

Provide structured bounds keyed by marker code:
"markerRanges": {
  "ALT": { "unit": "U/L", "lowerBound": 7, "upperBound": 56 }
}
  • lowerBound must be ≤ upperBound.
  • unit must match the preset unit for that code (after normalization).
  • When a code is listed, value validation uses your bounds instead of the default analytical preset.
  • Unknown keys in markerRanges fail validation.
  • Omit markerRanges entirely to use server preset analytical ranges only (still validates units, panel size, and duplicates).

Options

"options": {
  "requestedOutputs": ["inference_score_v1", "json", "html"],
  "requestedOutcomes": [
    "wellbeing.cardiovascular",
    "wellbeing.cognitive",
    "wellbeing.renal",
    "wellbeing.respiratory"
  ]
}
FieldDescription
requestedOutputsRequired array (min 1): inference_score_v1, score, json, pdf, html
requestedOutcomesOptional, max 8 entries. Public ids: wellbeing.cardiovascular, wellbeing.renal, wellbeing.cognitive, wellbeing.respiratory, wellbeing.general. Synonyms accepted: wellbeing.kidney, renal.wellbeing (renal). Legacy aliases (e.g. cardiovascular_wellbeing_10y) still normalize server-side. When omitted, the platform resolves defaults from configuration.
Public outcome ids match GET /v1/resultsoutcomes[].outcomeId.

Validation errors

StageHTTPerror.codedetails shape
Zod canonical body400VALIDATION_ERRORZod flatten: formErrors, fieldErrors
partnerId ≠ API key credential403PARTNER_ID_MISMATCH
14-marker panel (codes, units, bounds)400SUBMISSION_VALIDATION_ERRORArray of { field, message }
See Errors for examples.

Further reading