Getting Started

This page gets you from zero to a successful POST /v1/oura/recommendation call in one cURL.

You need

  1. 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.
  2. The sandbox base URL: https://partners.collectivex.health (same URL for sandbox + production — your key's tenant scope determines which environment you reach).
  3. curl and 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

The auto-generated OpenAPI reference is hosted at partnerdocs.collectivex.health alongside this written guide. Rebuilt on every change to the gateway docs.