Getting Started
This page gets you from zero to a successful POST /v1/oura/recommendation call in one cURL.
You need
- Your partner API key. You received it from CollectiveX Health out-of-band (encrypted channel). It starts with
zpka_. Do not commit it to source control. - The sandbox base URL:
https://partners.collectivex.health(same URL for sandbox + production — your key's tenant scope determines which environment you reach). curland a shell.
Minimal working request
Copy-paste this. Replace $CXH_API_KEY and $CXH_BASE_URL with your values.
export CXH_API_KEY="zpka_REPLACE_ME"
export CXH_BASE_URL="https://partners.collectivex.health"
curl -sS -X POST "$CXH_BASE_URL/v1/oura/recommendation" \
-H "Authorization: ApiKey $CXH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"request_id": "550e8400-e29b-71d4-a716-446655440000",
"timezone": "Europe/London",
"safety_mode": "permissive",
"minimum_confidence": 0.5,
"user_context": {
"cycle_phase": "luteal",
"cycle_day": 22,
"life_stage": "reproductive"
},
"oura": {
"sleep": [
{
"day": "2026-04-23",
"total_sleep_duration": 27000,
"average_hrv": 48.5
}
]
}
}'
Expected response
HTTP 200 OK. Body:
{
"request_id": "550e8400-e29b-71d4-a716-446655440000",
"recommendation": "Your HRV trend this week is trending healthy for your luteal phase. Consider ...",
"citations": [
{
"citation_id": "CXH-01HQRS4A8NZX2K3V5W7Y9B1M3P",
"source_title": "Heart Rate Variability and the Menstrual Cycle ...",
"source_url": "https://...",
"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-oura-v1.3"
}
Up to 7 citations per response. citations is empty when recommendation is null and suppression_reason is populated.
What could go wrong on first run
First request returning 403
consent_not_provisioned? This is the most common day-zero failure. Your API key was issued but consent provisioning is a separate manual step on the CollectiveX side — they don't happen together. Reply to whoever delivered your key and ask for consent activation. The 403 is not a code bug; nothing to fix client-side.
| HTTP | detail or error.code |
Fix |
|---|---|---|
401 Unauthorized |
Zuplo problem+json, detail: "No Authorization Header" / "Invalid Authorization Scheme" / "Authorization Failed" |
API key is missing, wrong scheme, malformed, revoked, or scoped to a different env. See errors.md for the full envelope. Use ApiKey scheme — Bearer is rejected. |
403 Forbidden |
Zuplo problem+json, detail: "missing_partner_id" |
Unusual — Zuplo normally injects X-Zuplo-Partner-Id for you. Contact support (support.md) with trace.requestId. |
403 Forbidden |
{"detail": "consent_not_provisioned"} |
Most common first-run failure. No consent record exists for your tenant yet — provisioning is a separate step from key issuance. Reply to your key delivery email asking for consent activation. |
403 Forbidden |
{"detail": "consent_revoked"} |
Active consent was revoked. Contact support — this is a contract-level event. |
422 Unprocessable Entity |
flat envelope, error.code = "invalid_request" |
Body schema is wrong. See request-response-reference.md for the full contract. |
422 Unprocessable Entity |
flat envelope, error.code = "out_of_scope" |
Query is clinical/diagnostic (triggered in safety_mode: strict). Use safety_mode: permissive for non-clinical wellness queries. |
429 Too Many Requests |
Zuplo-emitted body; Retry-After header |
Rate limit: 30 req/min per key. Backoff per the Retry-After header. See rate-limits.md. |
502 Bad Gateway |
flat envelope, error.code = "persistence_failed" |
Transient upstream write failure. Retry the same request_id after 1s — idempotency will serve the cached result if the write succeeded on retry. |
Flat envelope shape (used by all 422 / 500 / 502 responses):
{
"request_id": "<echoed>",
"trace_id": "<cxh-generated>",
"error": { "code": "<code>", "message": "<human-readable>", "details": null }
}
Three envelope shapes total — flat (origin), Zuplo problem+json (gateway 401/403), simple {"detail": "..."} (origin consent 403). errors.md walks through each.
Tenant pre-flight (GET /v1/health/me)
Before chasing imaginary client bugs, hit:
curl -sS "$CXH_BASE_URL/v1/health/me" \
-H "Authorization: ApiKey $CXH_API_KEY"
A 200 returns:
{
"tenant_id": "cxh-sandbox-oura",
"consent_active": true,
"scopes": ["v1.oura.recommendation"],
"rate_limit_per_min": 30
}
If consent_active is false, no /v1/oura/recommendation call will succeed — fix consent provisioning before anything else. This endpoint costs nothing — it doesn't burn a recommendation budget.
What next
- request-response-reference.md — full request body contract, enum tables, response fields, suppression semantics.
- authentication.md — API key format, rotation, revocation.
- errors.md — full error walkthrough with retry guidance per status.
- idempotency.md — safe retries and the 5-minute cache.
- rate-limits.md — 30 req/min, backoff recommendations.
The auto-generated OpenAPI reference is hosted at partnerdocs.collectivex.health alongside this written guide. Rebuilt on every change to the gateway docs.