CollectiveX Health Partner API
  • Get Started
  • Reference
  • Operations
  • API Reference
AuthenticationRequest / Response ReferenceErrorsIdempotencyRate Limits
powered by Zuplo
Reference

Request / Response Reference

Full body contract for POST /v1/TENANT/recommendation.

Pure-metrics-only contract

We accept only raw scientific measurements. Proprietary aggregate scores — score, hrv_balance, activity_balance, sleep_balance, or any nested contributors.*_balance — are accepted at the wire (so SDK drift doesn't break requests) and silently discarded. They are not persisted, not read by the recommendation engine, and not returned in the response.

Why: peer-reviewed clinical literature grounds against raw scientific units (HRV rMSSD in ms, heart rate in bpm, sleep-stage durations in seconds, raw temperatures, raw SpO₂). Vendor 0–100 buckets are opaque proprietary transforms that differ across vendors and firmware versions. Building against raw metrics keeps the contract portable: the same body shape will apply when Whoop, Apple Health, or Garmin onboard.

Keep sending the raw fields. If your SDK surfaces aggregate scores in the same payload, leaving them in is fine — they land in a permissive-extras bucket and are dropped before storage.

Request body

Top-level fields

FieldTypeRequiredNotes
request_idUUID v4/v7 string✅Idempotency key. See idempotency.md. Recommended: UUID v7.
querystring❌Free-text question. Omit for implicit-trigger (CxH auto-generates a relevant prompt from signals).
client_trace_idstring❌Your own trace correlation ID. Echoed back in telemetry, not in the response body.
timezoneIANA string✅E.g. "Europe/London", "America/Los_Angeles", "UTC". Abbreviations (EST, PST, CET) are rejected — see "Enum and range tables" below.
safety_modeenum✅"strict" or "permissive". See table below.
minimum_confidencefloat✅0.0–1.0. Your per-request floor — responses below this confidence are suppressed with suppression_reason: "below_client_threshold".
user_contextobject✅See "user_context" below.
TENANTobject✅See "TENANT payload" below.
personal_infoobject❌Optional partner-provided user metadata. See "personal_info" below.

user_context

FieldTypeRequiredNotes
cycle_phaseenum✅"menstrual" / "follicular" / "ovulatory" / "luteal". 4 values only.
cycle_dayint✅1–40. Days since last period start.
life_stageenum✅"reproductive" / "perimenopause" / "postmenopause". 3 values only.

TENANT payload

At least one signal-bearing key must be populated. Signal-bearing keys:

  • sleep — array of SleepRecord
  • heartrate — array of HeartRateBlock
  • vo2_max — array of Vo2MaxRecord
  • daily_readiness — array of DailyReadinessRecord
  • daily_sleep — array of DailySleepRecord
  • workout — array of WorkoutRecord
  • session — array of SessionRecord

The fields above describe the alpha Oura integration's body schema. Each partner has a dedicated route at /v1/<their-slug>/recommendation with its own body shape — biomarker fields will differ across partners. The auto-generated /api reference is the wire-format ground truth for each live partner deployment. The surrounding contract patterns — auth, idempotency, errors, suppression, citation semantics — are partner-agnostic and apply uniformly.

Secondary (optional) keys: tag, enhanced_tag, rest_mode_period, ring_configuration, daily_cardiovascular_age, daily_stress, daily_resilience, daily_spo2.

The schema allows extra fields (model_config = {"extra": "allow"}). Any field not in the table — including aggregate score and contributors — is silently dropped.

Example TENANT.sleep[0] entry (pure metrics)

Code
{ "day": "2026-04-23", "bedtime_start": "2026-04-22T23:14:00+01:00", "bedtime_end": "2026-04-23T07:32:00+01:00", "total_sleep_duration": 27720, "rem_sleep_duration": 5820, "deep_sleep_duration": 4680, "light_sleep_duration": 17220, "awake_time": 1080, "average_hrv": 48.5, "average_heart_rate": 54, "lowest_heart_rate": 48, "average_breath": 14.8, "temperature_deviation": -0.12 }

Example TENANT.daily_readiness[0] entry (post-ZUP-12 drop)

Code
{ "day": "2026-04-23", "timestamp": "2026-04-23T07:32:00+01:00" }

Per ZUP-12 (schema change, shipped 2026-04-24), daily_readiness no longer models score or contributors fields. If your SDK includes them, you may send them — they'll be dropped silently. The recommendation engine grounds on the raw metrics in sleep, heartrate, vo2_max, etc. instead.

personal_info (optional)

FieldTypePersisted?Notes
age_bucketstring✅E.g. "25-34". Bucketed age; raw DOB/age is not accepted.
emailstring❌ NOT persistedAccepted at the wire, scrubbed before the audit-trail write. Send it if your SDK forces you to; don't rely on it round-tripping. Do not use it as a lookup key.
Other partner-defined fieldsanymostly ✅PHI minimization drops the email field only; everything else currently persists. If you send raw DOB/SSN/etc., don't — we treat the partner as responsible for not sending PII we didn't request.

Enum and range tables

FieldValid values
user_context.cycle_phasemenstrual, follicular, ovulatory, luteal
user_context.life_stagereproductive, perimenopause, postmenopause
user_context.cycle_dayinteger in [1, 40] inclusive
safety_modestrict, permissive
minimum_confidencefloat in [0.0, 1.0] inclusive
timezoneIANA region/city format (Europe/London, Australia/Sydney, America/Los_Angeles) OR the bare strings UTC / GMT. Abbreviations like EST, PST, CET, JST are rejected with 422 invalid_request — they're ambiguous across DST boundaries.

safety_mode semantics

ModeBehavior
strictClinical-style queries (containing keywords matching internal diagnostic/prescriptive patterns) are rejected with 422 out_of_scope. Use when the integration is surfaced to consumers without a wellness disclaimer.
permissiveAll wellness queries accepted. Clinical framing in the response is still suppressed by the recommendation engine — but the request is processed. Use for partner-internal research, product testing, or consumer surfaces that include the CxH wellness disclaimer.

Response body

Success (200)

Code
{ "request_id": "550e8400-e29b-71d4-a716-446655440000", "recommendation": "Your HRV trend this week is trending healthy for your luteal phase. ...", "citations": [ { "citation_id": "CXH-01HQRS4A8NZX2K3V5W7Y9B1M3P", "source_title": "Heart Rate Variability and the Menstrual Cycle: A Systematic Review", "source_url": "https://doi.org/10.xxxx/xxxxx", "relevance": 0.91 } ], "recommendation_confidence": 0.78, "suggested_questions": [ "How does my HRV compare to last week?", "Should I adjust training intensity tonight?" ], "trace_id": "01HQRS4A8NZX2K3V5W7Y9B1M3P", "served_at": "2026-04-24T18:42:17.431Z", "model_version": "cxh-recommendation-v1.3", "suppression_reason": null, "warnings": [] }
FieldTypeNotes
request_idstringEchoed from the request.
recommendationstring | null400–1500 chars when present. null when suppressed (see below).
citationsarrayUp to 7 Citation objects, ordered by relevance descending. Empty when suppressed. See Citation object below.
recommendation_confidencefloat0.0–1.0. The engine's self-assessment of retrieved-evidence quality. Always returned, even when suppressed. See Recommendation confidence below.
suggested_questionsarray0–5 follow-up prompts. Primary payload in Shape B (implicit-trigger) responses.
trace_idstringOpaque CxH telemetry correlation ID. Include in support tickets.
served_atISO 8601 UTCResponse-emit timestamp.
model_versionstringOpaque version string. Changes on model/prompt updates — don't parse.
suppression_reasonstring | nullSee table below. null on successful recommendation.
warningsarraySoft-default advisory signals. V1 code: unmapped_persona — persona fell outside the known table and was routed to a safe default. Non-breaking-additive field — expect new codes over time.

Citation object

Each entry in the citations array carries the following fields:

FieldTypeNotes
citation_idstringStable identifier for the source document. ULID-style — safe to use as a deduplication key across responses (the same paper cited in two responses gets the same id).
source_titlestringArticle title from the source.
source_urlstringDOI URL when available, otherwise the canonical source URL. Always a fully qualified https:// URL.
relevancefloat0.0–1.0. Query-document match score from a biomedical cross-encoder reranker. See "Relevance score" below.

Relevance score

relevance is the per-citation match score from a domain-specialized biomedical cross-encoder (MedCPT — trained on PubMed query-article pairs) applied to your query (or, for Shape B, the implicit prompt CxH generates) and the candidate source paragraph.

What it measures. Query-document semantic relevance — how well the retrieved passage answers the query. Not cohort match — i.e. it does not encode how closely the study population in the source matches the user's demographic profile. The score is a raw cross-encoder output clamped to [0.0, 1.0].

How to interpret. A relative ranking signal within a single response — not a calibrated probability. 0.91 does not mean "91% confidence"; it means the reranker placed this passage strongly above its alternatives for this query. Citations in a single response are returned sorted by relevance descending — the first entry is the strongest match.

How to use it.

  • Display alongside the citation as a "match strength" indicator if your UI surfaces evidence to the user.
  • Threshold-filter client-side if you want to show only the top few; we return up to 7 regardless of value.
  • Don't compare values across different model_version strings — the reranker can change without a wire-format break, and absolute numbers may shift.

Caveat. Calibration of these scores is under active review. Treat values as relative ordering within a response, not as cross-response confidence intervals. If you need a single confidence number for the whole answer, see top-level recommendation_confidence below.

Recommendation confidence

recommendation_confidence is the engine's self-assessment of whether the retrieved evidence is strong enough to support an answer. Range [0.0, 1.0]. Returned on every response (served or suppressed) — always parse it.

What it measures. A weighted LLM-graded score across three criteria of the retrieved evidence:

  • Coverage (50%) — does the retrieved evidence actually address the user's question?
  • Specificity (30%) — is it relevant to this user's context (cycle phase, life stage, biomarkers), or generic?
  • Consistency (20%) — do the retrieved sources agree, or contradict?

For multi-step queries, per-sub-query scores are aggregated via min() — the weakest sub-query dominates. By design: a high overall score requires every sub-query to clear the bar.

How to interpret. A heuristic, not a calibrated probability. 0.78 does not mean "78% chance the recommendation is correct"; it means the engine judged the supporting evidence strong but not exceptional. The CxH platform floor is currently 0.5 — anything strictly below that is suppressed with suppression_reason: "below_platform_threshold".

How to use it.

  • Set minimum_confidence on your request to enforce a per-request floor; 0.5 is a reasonable starting point and matches the platform floor.
  • If your UI surfaces a "confidence" indicator, display the value but don't render it as a percentage — that mis-communicates certainty to end users.
  • Always parse it. A suppressed response still carries the score (e.g. 0.42 with suppression_reason: "below_platform_threshold") — useful for telemetry on which queries are being filtered.

Caveats.

  • Calibration is heuristic, not validated against partner-user outcomes. Treat values as relative ordering; absolute meanings may shift across model_version updates.
  • Exact 0.0 with recommendation: null and no suppression_reason is an internal failure path, not a low-evidence path. Surface as an error condition rather than "low confidence" in your UI.
  • The min() aggregation means one weak sub-query in a multi-step query can tank an otherwise-strong response. High relevance on returned citations + low recommendation_confidence is the signature of this case — not a contradiction.

Suppression

The recommendation engine has two confidence floors:

  1. Platform floor (CxH-side, non-configurable) — minimum confidence required for CxH to assert a wellness recommendation at all.
  2. Client floor (minimum_confidence on the request) — your per-request floor.

If recommendation_confidence is below either floor, the response is 200 OK with recommendation: null and one of:

suppression_reasonWhen
below_platform_thresholdBelow CxH-side platform floor. CxH decided we can't responsibly answer.
below_client_thresholdAbove platform floor but below your request's minimum_confidence. Your filter, not ours.
insufficient_signalNot enough partner data (e.g. only one day of sleep, no HRV). Re-send with more TENANT.* history if available.

Suppressed responses still return recommendation_confidence, suggested_questions, trace_id, served_at, model_version, and warnings. recommendation and citations are empty.

Shape A vs Shape B

The response shape is the same in both cases — what changes is which fields are primary for partner display:

  • Shape A (explicit query, query field present): recommendation is the primary payload. suggested_questions are 0–5 optional follow-ups.
  • Shape B (implicit trigger, query omitted): suggested_questions is the primary payload — these are CxH-generated prompts the user can tap. recommendation may be null even when confidence is high (we don't proactively answer a question the user didn't ask).

Your integration decides which shape to use based on the UI surface.

Sample full request (Shape B / implicit trigger)

Code
{ "request_id": "01HQRS4A8NZX2K3V5W7Y9B1M3P", "timezone": "Europe/London", "safety_mode": "permissive", "minimum_confidence": 0.5, "user_context": { "cycle_phase": "follicular", "cycle_day": 8, "life_stage": "reproductive" }, "TENANT": { "sleep": [ { "day": "2026-04-23", "total_sleep_duration": 27720, "rem_sleep_duration": 5820, "deep_sleep_duration": 4680, "average_hrv": 48.5, "average_heart_rate": 54 } ], "daily_readiness": [ {"day": "2026-04-23", "timestamp": "2026-04-23T07:32:00+01:00"} ], "daily_sleep": [ {"day": "2026-04-23", "timestamp": "2026-04-23T07:32:00+01:00"} ] }, "personal_info": { "age_bucket": "25-34" } }

Note: no query field. The response will have suggested_questions populated and recommendation may be null — that's expected Shape B behavior.

What's not in this reference

  • The formal JSON Schema is at docs.collectivex.health/api (auto-generated from the OpenAPI spec, kept in sync on every change). Use it for code generation.
  • Error response shapes — see errors.md.
  • Retry semantics — see idempotency.md.
Last modified on April 29, 2026
AuthenticationErrors
On this page
  • Pure-metrics-only contract
  • Request body
    • Top-level fields
    • user_context
    • TENANT payload
    • personal_info (optional)
    • Enum and range tables
    • safety_mode semantics
  • Response body
    • Success (200)
    • Citation object
    • Recommendation confidence
    • Suppression
    • Shape A vs Shape B
  • Sample full request (Shape B / implicit trigger)
  • What's not in this reference
JSON
JSON
JSON
JSON