LiveDigital MSE™ API is now availableRead the docs

API Reference

Attunio Clinical Intelligence API

On this page

Quickstart

One authenticated POST turns the timing telemetry you already collect into structured, clinician-reviewable Digital MSE™ signals. Grab a test key from your developer dashboard, send the request below, and read the response — no SDK required.

1 · Send signals

POST /v1/dmse/analyze
curl https://attuniohealth.com/api/v1/dmse/analyze \  -H "Authorization: Bearer att_sk_test_xxx" \  -H "Content-Type: application/json" \  -d '{    "responseLatencySeconds": 2.6,    "speechRateWpm": 98,    "psychomotorFidgetEpisodes": 4,    "sustainedAttentionPct": 66,    "explain": true  }'

2 · Get intelligence back

200 OK
{  "ok": true,  "requestId": "req_9f2c41d07ab34e8a91b2",  "data": {    "object": "dmse.analysis",    "summary": {      "status": "clinical_attention",      "headline": "4 of 4 signals outside typical range — Response latency, Speech rate, Psychomotor activity, Sustained attention (high confidence).",      "flagged": ["Response latency", "Speech rate", "Psychomotor activity", "Sustained attention"],      "requiresClinicianReview": true    },    "signalsPresent": 4,    "overallConfidence": "high",    "signals": [      {        "signalKey": "response_latency",        "metricValue": 2.6,        "interpretation": "clinical_attention",        "referenceRange": "<1.8s typical; 1.8–2.4s subclinical; ≥2.5s clinical attention",        "mseLine": "Speech: increased response latency (avg ~2.6s)."      }      // ... psychomotor, speech_rate, sustained_attention    ],    "mseNarrative": ["Speech: increased response latency (avg ~2.6s).", "..."],    "intelligence": {      "reasoning": {        "narrative": "Current findings suggest a pattern warranting clinical attention. Objective behavioral indicators, including response latency averaging 2.6 seconds against a 2.5-second threshold, demonstrate a consistent departure from typical range across all four measured signals. Confidence is high given complete session data, and the existing evidence supports clinician review of this result.",        "generatedAt": "2026-07-03T09:00:00Z"      }    }  }}

That is the entire loop. The full field reference, the other four intelligence endpoints, errors, and rate limits are documented below.

Why Attunio — instead of building it yourself

Every endpoint is a deterministic, explainable engine, not a black box. You could wire up your own heuristics — here is what you would have to build, validate, and maintain to match what ships today.

Validated clinical thresholds

Every signal fires against thresholds grounded in clinical literature and tuned on real visit data — not arbitrary cutoffs you would have to defend in a chart review.

MSE-mapped outputs

Outputs map directly to mental-status-exam language clinicians already document, so results drop into existing workflows instead of becoming another uninterpretable score.

Longitudinal baselines & trend

Send patient history and each signal is contextualized against that person's own baseline with mean, SD, and trend direction — the longitudinal layer is the hard part to build well.

HIPAA-ready by construction

No raw audio or video is ever accepted or stored, traffic is TLS 1.3, and every output is decision support for clinician review — never an autonomous diagnosis.

A suite, not a single call

Digital MSE™ is API #1. Medication, Biomarker, Outcome, and Relapse Intelligence reuse the same auth and envelope, so you integrate once and grow into the rest.

Maintained & versioned for you

Threshold updates, new biomarkers, and model improvements ship behind a stable, forward-compatible contract — you get the upgrades without re-validating your own engine.

Overview

The Attunio Clinical Intelligence API is a secure, RESTful service for high-throughput, real-time behavioral telemetry processing. It accepts structured speech- and movement-timing metrics from third-party video visits and returns Digital MSE™ output: four structured signals, an MSE narrative, confidence, and optional per-patient baseline and trend.

Data minimization. The API strictly processes downstream structured numerical tracking coordinates and speech-cadence vectors. No raw video or audio files are ever accepted, stored, or written to disk. Output is decision support for clinician review — not an autonomous diagnosis, biometric ML, or affect recognition.

Base URL: https://attuniohealth.com/api/v1

Intelligence suite

The platform is organized as a suite of clinical-intelligence products that share one authentication model and one response envelope. All five products are live and callable today, each backed by a deterministic, explainable engine — every signal is traceable to its source data, never a black-box prediction. Reference docs for each endpoint follow below.

ProductStatusEndpointWhat it does
Digital MSE™LivePOST /v1/dmse/analyzeSpeech & movement timing → mental-status-exam signals.
Medication Intelligence™LivePOST /v1/medication/analyzeAdherence & response patterns from medication history.
Biomarker Intelligence™LivePOST /v1/biomarker/analyzeLabs & wearable biomarkers, contextualized.
Outcome Intelligence™LivePOST /v1/outcome/scoreSymptom scales → outcome trajectories.
Relapse Prediction™LivePOST /v1/relapse/predictMulti-signal early-warning system.

Authentication

Authenticate every request with an API key issued from your developer dashboard. Pass it as a Bearer token. Keys are shown once at creation — store them securely and never embed them in client-side code.

Header
Authorization: Bearer att_sk_live_a1b2c3d4e5f6...

Test keys (att_sk_test_…) run against the sandbox; live keys (att_sk_live_…) are issued to production apps.

Publishable keys (browser)

Secret keys (att_sk_…) carry full API access and must stay server-side. To call the API directly from the browser — for example from a capture SDK running inside a telehealth video session — use a publishable key (att_pk_…) instead.

Publishable keys are deliberately constrained so they're safe to ship to the client:

  • They can only call POST /v1/dmse/analyze. Every other endpoint returns 403 forbidden.
  • They're honored only when the request Originis in the key's allow-list, which you manage in the dashboard. A leaked key can't be used from any other site.
  • The endpoint supports CORS preflight, so browser requests work without a proxy.
Browser fetch
// Runs in the browser — publishable keys are safe to ship to the client.// The key only works from origins you allow-list in the dashboard.const res = await fetch("https://attuniohealth.com/api/v1/dmse/analyze", {  method: "POST",  headers: {    "Authorization": "Bearer att_pk_live_xxx",    "Content-Type": "application/json",  },  body: JSON.stringify({    responseLatencySeconds: 2.6,    speechRateWpm: 98,    psychomotorFidgetEpisodes: 4,    sustainedAttentionPct: 66,  }),}) const { data } = await res.json()

A request from an origin that isn't allow-listed is rejected:

403 Forbidden
{  "ok": false,  "requestId": "req_7c1e58f2a90b4d3e62f4",  "error": {    "code": "forbidden",    "message": "This origin is not allowed for this publishable key. Add it to the key's allowed origins in the developer dashboard.",    "docs": "https://attunio.com/developers/docs#errors"  }}

Note: raw audio and video never touch Attunio. Extraction happens on-device; only the derived timing telemetry shown above is sent to the API.

Capture SDK (@attunio/capture)

The capture SDK is the fastest way to go from a telehealth video session to Digital MSE™ signals. It takes the WebRTC MediaStream your platform already produces, extracts speech rate and response latency on-device, and calls /v1/dmse/analyze with a publishable key. Raw audio never leaves the browser.

Install
npm install @attunio/capture
Basic usage
import { AttunioSession, fromUserMedia } from '@attunio/capture' // Publishable key — safe to ship in browser code.const session = new AttunioSession({ publishableKey: 'att_pk_live_xxx' }) // Any WebRTC MediaStream works. Here: the patient's mic.const stream = await fromUserMedia({ audio: true })session.attach(stream)               // audio is processed on-device // speech rate + response latency are extracted locally.// Optionally merge in video-derived signals you already compute:session.setVideoSignals({ psychomotorFidgetEpisodes: 4, sustainedAttentionPct: 72 }) const dmse = await session.analyze() // POSTs derived numbers, returns Digital MSEsession.stop()

Video-derived signals (psychomotor episodes, sustained attention) are not extracted in the browser today — they are API-only. If your own pipeline produces them, pass them through with setVideoSignals()and they're merged into the same call.

Each platform integration is just a one-liner that resolves the stream — the on-device extraction is identical everywhere:

Platform adapters
// Each platform just resolves the MediaStream you already hold:import {  fromZoomTracks,    // Zoom Apps SDK media tracks  fromDailyParticipant, // Daily local participant  fromTwilioTracks,  // Twilio LocalAudioTrack / LocalVideoTrack  fromVonagePublisher,  // Vonage / OpenTok publisher  streamFromTracks,  // compose from raw MediaStreamTracks} from '@attunio/capture' session.attach(fromDailyParticipant(call.participants().local))

Want to see it work? Run the live demo — it captures your microphone and returns real Digital MSE output. For platform-by-platform setup, see Integrations.

Health check — GET /v1/ping

Confirms your key and connectivity before sending telemetry.

GET /v1/ping
curl https://attuniohealth.com/api/v1/ping \  -H "Authorization: Bearer att_sk_test_xxx"

Analyze telemetry — POST /v1/dmse/analyze

Submit timing telemetry for a single session. Provide at least one metric; supply more for a fuller mental-status picture, and include history to unlock baseline and trend.

Request
curl https://attuniohealth.com/api/v1/dmse/analyze \  -H "Authorization: Bearer att_sk_test_xxx" \  -H "Content-Type: application/json" \  -d '{    "responseLatencySeconds": 2.6,    "speechRateWpm": 98,    "psychomotorFidgetEpisodes": 4,    "sustainedAttentionPct": 66,    "sessionDurationSeconds": 540,    "history": {      "response_latency": [3.1, 2.9, 2.7]    },    "sessionRef": "visit_8841"  }'
200 OK
{  "ok": true,  "requestId": "req_9f2c41d07ab34e8a91b2",  "data": {    "object": "dmse.analysis",    "sessionRef": "visit_8841",    "generatedAt": "2026-06-28T15:04:05.000Z",    "summary": {      "status": "clinical_attention",      "headline": "4 of 4 signals outside typical range — Response latency, Speech rate, Psychomotor activity, Sustained attention (high confidence).",      "flagged": ["Response latency", "Speech rate", "Psychomotor activity", "Sustained attention"],      "requiresClinicianReview": true    },    "signalsPresent": 4,    "overallConfidence": "high",    "signals": [      {        "signalKey": "response_latency",        "label": "Response latency",        "present": true,        "metricValue": 2.6,        "metricUnit": "s",        "interpretation": "clinical_attention",        "referenceRange": "<1.8s typical; 1.8–2.4s subclinical; ≥2.5s clinical attention",        "detail": "Markedly delayed response latency (avg 2.6s).",        "confidence": "high",        "trendDir": "down",        "mseLine": "Speech: increased response latency (avg ~2.6s).",        "baseline": { "mean": 2.9, "sd": 0.2, "n": 3 }      }      // ... psychomotor, speech_rate, sustained_attention    ],    "mseNarrative": [      "Speech: increased response latency (avg ~2.6s).",      "Psychomotor: psychomotor agitation (4 fidget episodes during session)."    ],    "interpretationScale": {      "within_normal": "Within typical range — no action suggested.",      "subclinical": "Mildly outside typical range — worth noting, not urgent.",      "clinical_attention": "Clearly outside typical range — flag for clinician review."    },    "disclaimer": "Deterministic, rules-based decision support derived from numerical timing metrics. Not biometric ML, affect recognition, or an autonomous diagnosis. For clinician review only."  }}

Request fields

FieldTypeDescription
psychomotorFidgetEpisodesnumberDiscrete fidget / motor-restlessness episodes counted during the session (0–1000).
responseLatencySecondsnumberMean conversational response latency in seconds (0–120).
speechRateWpmnumberMean speech rate in words per minute (0–600).
sustainedAttentionPctnumberShare of session with gaze on-screen, 0–100.
sessionDurationSecondsnumberOptional. Session length; longer sessions raise confidence.
historyobjectOptional. Arrays of prior values keyed by signal (psychomotor, response_latency, speech_rate, sustained_attention) for baseline + trend.
sessionRefstringOptional. Your correlation id, echoed back. Never stored.

Shared objects

The Medication, Outcome, and Relapse endpoints accept the same Assessment objects, Medication accepts Prescription objects, and Biomarker accepts Lab panel objects containing Biomarker readings. They are documented once here and referenced from each endpoint below. All date fields are ISO-8601 strings; ? marks an optional field.

Assessment object

FieldTypeDescription
instrumentIdstringStable id for the instrument, e.g. "phq9". 1–64 chars.
instrumentNamestringHuman-readable name, e.g. "PHQ-9". 1–120 chars.
scorenumberRaw score for this administration. 0–1000.
maxScorenumberMaximum possible score for the instrument. 1–1000.
severitystring?Optional severity band, e.g. "moderately severe".
createdAtISO dateWhen the assessment was administered.

Prescription object

FieldTypeDescription
idstringYour prescription id; echoed back as the signal's sourceRef.
medicationNamestringMedication name, e.g. "Sertraline". 1–160 chars.
dosagestring?Optional dosage, e.g. "50mg".
frequencystring?Optional frequency, e.g. "daily".
statusstringPrescription status, e.g. "active" or "discontinued".
prescribedAtISO date?When the medication started. Anchors the before/after symptom comparison.

Lab panel object

FieldTypeDescription
idstringYour lab/panel id; echoed back as the signal's sourceRef.
labNamestringLab or panel name, e.g. "Quest Diagnostics". 1–160 chars.
observedAtISO date?When the panel was drawn or observed.
results.biomarkersBiomarker[]Up to 200 biomarker readings (see Biomarker object).

Biomarker object

FieldTypeDescription
namestringBiomarker name, e.g. "TSH". 1–120 chars.
valuenumber | string | nullThe measured value.
unitstring?Unit of measure, e.g. "mIU/L".
flagstring?"high" | "low" | "critical" | "normal". Only abnormal flags produce a signal.
referenceRangestring?Reference range string, e.g. "0.4-4.0".
notestring?Optional free-text note (≤240 chars).

Medication Intelligence™ — POST /v1/medication/analyze

Correlates each active prescription's start date with the symptom-scale trajectory of the most-covered instrument (before vs. after the med started), plus an optional Digital MSE™ latency change, to produce explainable response and monitoring signals. Send prescriptions, assessments, and optionally dmseLatencyPctChange.

Request
curl https://attuniohealth.com/api/v1/medication/analyze \  -H "Authorization: Bearer att_sk_test_xxx" \  -H "Content-Type: application/json" \  -d '{    "prescriptions": [      {        "id": "rx_001",        "medicationName": "Sertraline",        "dosage": "50mg",        "frequency": "daily",        "status": "active",        "prescribedAt": "2026-03-01T00:00:00Z"      }    ],    "assessments": [      { "instrumentId": "phq9", "instrumentName": "PHQ-9", "score": 18, "maxScore": 27, "createdAt": "2026-02-20T00:00:00Z" },      { "instrumentId": "phq9", "instrumentName": "PHQ-9", "score": 11, "maxScore": 27, "createdAt": "2026-05-15T00:00:00Z" }    ],    "dmseLatencyPctChange": -14  }'
200 OK
{  "ok": true,  "data": {    "object": "medication.analysis",    "generatedAt": "2026-06-28T15:04:05.000Z",    "signalsPresent": 2,    "signals": [      {        "module": "medication",        "signalKey": "med_response_sertraline",        "label": "Sertraline — symptom response",        "detail": "PHQ-9 improved 18→11 (39% reduction) since Sertraline 50mg started.",        "tone": "positive",        "metricValue": 11,        "metricUnit": "score",        "confidence": "moderate",        "summaryLine": "On Sertraline 50mg, PHQ-9 improved from 18 to 11 (39% reduction).",        "sourceRef": "rx_001",        "observedAt": "2026-05-15T00:00:00.000Z"      }      // ... objective Digital MSE™ corroboration signal    ],    "disclaimer": "..."  }}

Request body

FieldTypeDescription
prescriptionsPrescription[]Prescriptions to correlate. Up to 100. See Prescription object.
assessmentsAssessment[]Symptom-scale administrations. Up to 500. See Assessment object.
dmseLatencyPctChangenumber?Optional Digital MSE™ latency % change for objective corroboration. -100 to 1000.
sessionRefstring?Optional correlation id, echoed back. Never stored.

Biomarker Intelligence™ — POST /v1/biomarker/analyze

Maps abnormally-flagged lab biomarkers to their psychiatric relevance so reversible medical drivers are ruled out before escalating psychotropics. Only readings with a flag of high, low, or critical produce a signal; normal values are ignored.

Request
curl https://attuniohealth.com/api/v1/biomarker/analyze \  -H "Authorization: Bearer att_sk_test_xxx" \  -H "Content-Type: application/json" \  -d '{    "labs": [      {        "id": "lab_88",        "labName": "Quest Diagnostics",        "observedAt": "2026-06-01T00:00:00Z",        "results": {          "biomarkers": [            { "name": "TSH", "value": 6.8, "unit": "mIU/L", "flag": "high", "referenceRange": "0.4-4.0" },            { "name": "Vitamin D", "value": 18, "unit": "ng/mL", "flag": "low", "referenceRange": "30-100" }          ]        }      }    ]  }'
200 OK
{  "ok": true,  "data": {    "object": "biomarker.analysis",    "generatedAt": "2026-06-28T15:04:05.000Z",    "signalsPresent": 2,    "signals": [      {        "module": "biomarker",        "signalKey": "bio_tsh",        "label": "Thyroid (TSH)",        "detail": "6.8 mIU/L (ref 0.4-4.0) — elevated TSH can present as depression, fatigue, and cognitive slowing — screen before escalating an antidepressant.",        "tone": "watch",        "metricValue": 6.8,        "metricUnit": "mIU/L",        "confidence": "high",        "summaryLine": "Thyroid (TSH) elevated at 6.8 mIU/L: ...",        "sourceRef": "lab_88",        "observedAt": "2026-06-01T00:00:00.000Z"      }      // ... vitamin D signal    ],    "disclaimer": "..."  }}

Request body

FieldTypeDescription
labsLab[]At least one lab panel (required). Up to 100. See Lab panel object.
sessionRefstring?Optional correlation id, echoed back. Never stored.

Outcome Intelligence™ — POST /v1/outcome/score

Fuses up to five independent sources into a single treatment-response composite from -100 (worsening) to +100 (strong improvement). Each component's signed contribution is returned, and confidence scales with how many independent sources are available and agree.

Request
curl https://attuniohealth.com/api/v1/outcome/score \  -H "Authorization: Bearer att_sk_test_xxx" \  -H "Content-Type: application/json" \  -d '{    "assessments": [      { "instrumentId": "phq9", "instrumentName": "PHQ-9", "score": 18, "maxScore": 27, "createdAt": "2026-02-20T00:00:00Z" },      { "instrumentId": "phq9", "instrumentName": "PHQ-9", "score": 9, "maxScore": 27, "createdAt": "2026-05-15T00:00:00Z" }    ],    "dmseLatencyPctChange": -12,    "activeMedications": 1,    "biomarkerWatchCount": 0,    "avgSleepHours": 7.5  }'
200 OK
{  "ok": true,  "data": {    "object": "outcome.score",    "generatedAt": "2026-06-28T15:04:05.000Z",    "score": 66,    "direction": "improving",    "confidence": "high",    "components": [      { "key": "symptoms", "label": "Symptom scales", "contribution": 45, "detail": "PHQ-9 improved 18→9 across 2 administrations.", "available": true },      { "key": "dmse", "label": "Digital MSE™ latency", "contribution": 6, "detail": "Response latency changed -12% across visits.", "available": true }      // ... medication, biomarker, sleep components    ],    "summaryLine": "Treatment response is improving (composite +66) across 4 independent data sources.",    "sourceCount": 4,    "disclaimer": "..."  }}

Request body

FieldTypeDescription
assessmentsAssessment[]Symptom-scale administrations. Up to 500. See Assessment object.
dmseLatencyPctChangenumber?Optional Digital MSE™ latency % change across visits. -100 to 1000.
activeMedicationsintegerCount of active medications. 0–100. Default 0.
biomarkerWatchCountintegerCount of flagged biomarker watch-items. 0–100. Default 0.
avgSleepHoursnumber?Optional average nightly sleep hours from wearables. 0–24.
sessionRefstring?Optional correlation id, echoed back. Never stored.

Relapse Prediction™ — POST /v1/relapse/predict

The forward-looking layer: uses the velocity and recency of symptom scales, Digital MSE™ latency, wearable sleep, medication-change windows, and visit cadence to compute a transparent, additive 0–100 early-warning score. Every contributing factor and a conservative recommendation are returned — nothing is auto-actioned.

Request
curl https://attuniohealth.com/api/v1/relapse/predict \  -H "Authorization: Bearer att_sk_test_xxx" \  -H "Content-Type: application/json" \  -d '{    "assessments": [      { "instrumentId": "phq9", "instrumentName": "PHQ-9", "score": 12, "maxScore": 27, "createdAt": "2026-05-01T00:00:00Z" },      { "instrumentId": "phq9", "instrumentName": "PHQ-9", "score": 19, "maxScore": 27, "severity": "moderately severe", "createdAt": "2026-06-15T00:00:00Z" }    ],    "dmseLatencyPctChange": 16,    "avgSleepHours": 5.2,    "daysSinceMedChange": 12,    "hasActiveMedication": true,    "daysSinceLastVisit": 38  }'
200 OK
{  "ok": true,  "data": {    "object": "relapse.prediction",    "generatedAt": "2026-06-28T15:04:05.000Z",    "score": 68,    "level": "elevated",    "trend": "rising",    "confidence": "high",    "topDriver": "Symptom trajectory",    "factors": [      { "key": "symptoms", "label": "Symptom trajectory", "points": 34.4, "maxPoints": 38, "tone": "elevated", "available": true, "detail": "PHQ-9 rising 12→19/27 (moderately severe) at the two most recent administrations." },      { "key": "sleep", "label": "Sleep (wearable)", "points": 12, "maxPoints": 18, "tone": "elevated", "available": true, "detail": "Averaging 5.2h/night recently — short sleep is a leading relapse precursor." }      // ... dmse, medication, cadence factors    ],    "summaryLine": "Elevated relapse risk (68/100), signals rising, driven primarily by symptom trajectory.",    "recommendation": "Flag for closer monitoring; consider a check-in before the next scheduled visit.",    "sourceCount": 5,    "disclaimer": "..."  }}

Request body

FieldTypeDescription
assessmentsAssessment[]Symptom-scale administrations. Up to 500. See Assessment object.
dmseLatencyPctChangenumber?Optional Digital MSE™ latency % change across visits. -100 to 1000.
avgSleepHoursnumber?Optional average nightly sleep hours from wearables. 0–24.
daysSinceMedChangeinteger?Days since the last medication change. 0��3650.
hasActiveMedicationbooleanWhether the patient is on an active medication. Default false.
daysSinceLastVisitinteger?Days since the last clinical visit. 0–3650.
sessionRefstring?Optional correlation id, echoed back. Never stored.

Lab orders — /v1/labs

Unlike the Intelligence endpoints, the lab and wearable resources are stateful: they persist records keyed by your own patientRef — the identifier you already use for the patient in your system. These endpoints require a secret key (att_sk_); publishable browser keys are rejected.

1. Browse the catalog. GET /v1/labs/panels returns every orderable panel and its panelId. Filter with ?category=.

GET /v1/labs/panels
curl https://attuniohealth.com/api/v1/labs/panels \  -H "Authorization: Bearer att_sk_test_xxx"
200 OK
{  "ok": true,  "data": {    "object": "list",    "resource": "lab_panel",    "count": 24,    "panels": [      {        "panelId": "thyroid-panel",        "name": "Thyroid panel (TSH)",        "category": "Nutrients & hormones",        "description": "Thyroid dysfunction can mimic depression, anxiety, and fatigue.",        "priceCents": 5900,        "turnaround": "1-2 days",        "useCases": ["Depression", "Fatigue"]      }      // ... more panels    ]  }}

2. Create an order. POST /v1/labs/orders places the order in the requested state. A provider still authorizes it before specimen collection; the status advances through authorized → scheduled → collected → resulted → completed.

POST /v1/labs/orders
curl https://attuniohealth.com/api/v1/labs/orders \  -H "Authorization: Bearer att_sk_test_xxx" \  -H "Content-Type: application/json" \  -d '{    "patientRef": "patient_8842",    "panelId": "thyroid-panel",    "note": "Baseline before starting an SSRI.",    "metadata": { "encounterId": "enc_1201" }  }'
201 Created
{  "ok": true,  "data": {    "object": "lab_order",    "id": "labord_9f3c2a1b7d8e4f60a1c2d3e4f5061728",    "patientRef": "patient_8842",    "panelId": "thyroid-panel",    "panelName": "Thyroid panel (TSH)",    "category": "Nutrients & hormones",    "priceCents": 5900,    "status": "requested",    "note": "Baseline before starting an SSRI.",    "requisitionNumber": "ATN-LZ4K9-061728",    "metadata": { "encounterId": "enc_1201" },    "createdAt": "2026-07-03T15:04:05.000Z",    "updatedAt": "2026-07-03T15:04:05.000Z",    "notice": "Lab orders created through the API still require provider authorization ..."  }}

Request body

FieldTypeDescription
patientRefstringYour own patient identifier. Opaque to Attunio; used to scope and list the patient's orders. 1–128 chars.
panelIdstringId of an orderable panel from GET /v1/labs/panels, e.g. "thyroid-panel".
notestring?Optional PHI-free note stored with the order. Up to 500 chars.
metadataobject?Optional free-form key/value metadata, echoed back on the order.

3. List & retrieve. GET /v1/labs/orders lists your orders (filter with ?patientRef= and ?limit=, 1–200), and GET /v1/labs/orders/{id} fetches a single order. Both are scoped to your account.

GET /v1/labs/orders?patientRef=…
curl "https://attuniohealth.com/api/v1/labs/orders?patientRef=patient_8842" \  -H "Authorization: Bearer att_sk_test_xxx"

Wearable connections — /v1/wearables

Connect a patient's wearable (Apple Health, Garmin, Fitbit, Oura, WHOOP, Google Fit, Withings, and more) so their heart-rate, sleep, activity, and stress data can flow into Attunio. Connections are persisted per patientRef and require a secret key.

1. List supported providers. GET /v1/wearables/providers returns the provider codes you can pass to the connect endpoint.

2. Start a connection. POST /v1/wearables/connect returns a pending connection with a connect.url — an Attunio-hosted URL that carries the selected providers. Send the patient there (redirect or embed) to authorize their device; no third-party URL is exposed.

POST /v1/wearables/connect
curl https://attuniohealth.com/api/v1/wearables/connect \  -H "Authorization: Bearer att_sk_test_xxx" \  -H "Content-Type: application/json" \  -d '{    "patientRef": "patient_8842",    "providers": ["APPLE", "GARMIN", "OURA"],    "language": "en"  }'
201 Created
{  "ok": true,  "data": {    "object": "wearable_connection",    "id": "wear_1a2b3c4d5e6f708192a3b4c5d6e7f809",    "patientRef": "patient_8842",    "status": "pending",    "provider": null,    "providerDisplayName": null,    "connect": {      "url": "https://attuniohealth.com/connect/wearable/wear_1a2b3c4d5e6f708192a3b4c5d6e7f809?providers=APPLE,GARMIN,OURA"    },    "connectedAt": null,    "createdAt": "2026-07-03T15:04:05.000Z",    "updatedAt": "2026-07-03T15:04:05.000Z",    "notice": "... wearable connections require the patient to complete device authorization ..."  }}

Request body

FieldTypeDescription
patientRefstringYour own patient identifier. Used to scope and list the patient's connections. 1–128 chars.
providersstring[]?Optional subset of providers to offer, e.g. ["APPLE","GARMIN"]. Defaults to all supported providers.
languagestring?Optional connect-page language (ISO 639-1, 2 letters). Defaults to "en".
metadataobject?Optional free-form key/value metadata, echoed back on the connection.

3. Poll status. GET /v1/wearables/connections reflects the current state — pending, connected, or disconnected. Once the patient finishes authorizing, the connection flips to connected and the resolved provider is populated.

GET /v1/wearables/connections?patientRef=…
curl "https://attuniohealth.com/api/v1/wearables/connections?patientRef=patient_8842" \  -H "Authorization: Bearer att_sk_test_xxx"
200 OK
{  "ok": true,  "data": {    "object": "list",    "resource": "wearable_connection",    "count": 1,    "patientRef": "patient_8842",    "connections": [      {        "object": "wearable_connection",        "id": "wear_1a2b3c4d5e6f708192a3b4c5d6e7f809",        "patientRef": "patient_8842",        "status": "connected",        "provider": "OURA",        "providerDisplayName": "Oura Ring",        "connectedAt": "2026-07-03T15:22:10.000Z"        // ... connect, lastSyncAt, timestamps      }    ]  }}

Model versioning & calibration

Every scored response carries a model block so you always know exactly which version produced a result. The engines ship with clinically-reasoned default parameters, and Attunio periodically calibrates them against real, de-identified outcomes — but calibration only ever proposes a new version. Nothing changes what your integration receives until a licensed reviewer promotes it.

The model block (on every scored response)
{  "ok": true,  "data": {    "object": "relapse.prediction",    "model": {      "key": "relapse_risk",      "version": 4,          // null until a calibrated version is promoted      "source": "calibrated" // "default" = shipped constants, "calibrated" = clinician-promoted    },    "score": 68,    "level": "elevated"    // ... factors, recommendation, disclaimer  }}

model object

FieldTypeDescription
model.keystringStable id of the engine, e.g. "relapse_risk", "outcome_score", "medication_response", "biomarker_interpretation".
model.versioninteger | nullThe active version that produced this response. null means the engine is running the shipped defaults — no calibrated version has been promoted.
model.sourcestring"default" = the clinically-reasoned constants that ship with the API. "calibrated" = a version a licensed admin reviewed and promoted.

How calibration works — and what it is not. This is not autonomous learning or self-modifying clinical logic. It is a transparent, human-supervised tuning loop, and several guardrails keep it that way:

  • Defaults are identical until a human acts. A freshly provisioned engine returns source: "default" and behaves exactly as documented. Promotion is a deliberate, audited action by a licensed admin.
  • Calibration proposes, it never promotes. The job reads aggregate prediction-vs-outcome concordance and registers a candidate version with its measured metrics. A reviewer compares it against the active version and decides.
  • The same explainable engine, retuned. Calibration only adjusts bounded weights and thresholds within fixed safety caps — it does not introduce a black-box model. Every signal stays traceable to its source data, and abnormal-lab safety floors cannot be lowered.
  • Versioned and reversible. Exactly one version is active per engine at a time, and any prior version can be re-promoted instantly. The model.version on each response makes results auditable after the fact.

Pin to behavior you have validated by recording the model.version you tested against. Outputs remain clinician-reviewable decision support at every version — calibration sharpens the thresholds, it never turns a score into a diagnosis or an auto-action.

Unified intelligence schema

Every intelligence endpoint — Digital MSE™, Biomarker Intelligence™, Medication Intelligence™, Outcome Intelligence™, and Relapse Prediction™ — returns the same intelligence block alongside its endpoint-specific fields. Learn the six sub-blocks once and you can read every response on the platform. Existing fields are untouched: the block is purely additive, so current integrations keep working.

intelligence.* — identical on all five endpoints

FieldTypeDescription
schemastringAlways "attunio.intelligence/v1". Bump only on breaking changes.
enginestringWhich engine produced this: "dmse", "biomarker", "medication", "outcome", or "relapse".
summaryobjectstatus, one-sentence headline, flagged items (worst first), and requiresClinicianReview.
confidenceobjectlevel (high/moderate/low), score 0–1, and human-readable reasons for the rating.
dataQualityobjectcompleteness 0–1, missingInputs[], and freshness (newest input timestamp + stale inputs).
provenanceobjectgeneratedAt, engineVersion, method, and every input with its value, timestamp, and whether it contributed.
codesarraySNOMED CT / LOINC / RxNorm mappings with mapsTo pointers into the response.
changeobject | nullWhy the result differs from your previous call. Present only when the request includes `previous`.
interpretationScaleobjectLegend defining every status value this engine can return.
reasoningobject?Opt-in plain-language interpretation of the findings. Present only when the request sets `explain: true`; null if generation failed.
intelligence block (relapse example)
"intelligence": {  "schema": "attunio.intelligence/v1",  "engine": "relapse",  "summary": {    "status": "moderate",    "headline": "Relapse risk 38/100 (moderate) — sleep restored, follow-up gap closed.",    "flagged": ["Assessment trend"],    "requiresClinicianReview": true  },  "confidence": {    "level": "moderate",    "score": 0.62,    "reasons": [      "5 of 6 inputs supplied",      "Assessment history covers 2 points over 31 days"    ]  },  "dataQuality": {    "completeness": 0.83,    "missingInputs": ["biomarkerWatchCount"],    "freshness": { "newestInputAt": "2026-06-01T00:00:00Z", "staleInputs": [] }  },  "provenance": {    "generatedAt": "2026-07-03T15:04:05.000Z",    "engineVersion": "default",    "method": "deterministic_rules",    "inputs": [      { "name": "assessments", "value": "2 PHQ-9 points", "observedAt": "2026-06-01T00:00:00Z", "contributed": true },      { "name": "avgSleepHours", "value": 7.5, "observedAt": null, "contributed": true }    ]  },  "codes": [    { "system": "http://snomed.info/sct", "code": "225444004", "display": "At risk for relapse", "mapsTo": "summary.status" }  ],  "change": {    "direction": "improved",    "delta": -6,    "previousStatus": "elevated",    "currentStatus": "moderate",    "explanations": [      "Score moved from 44 to 38 (-6).",      "avgSleepHours improved from 5.1 to 7.5 — sleep disruption factor no longer firing.",      "daysSinceLastVisit dropped from 30 to 12 — follow-up gap factor cleared."    ],    "previousComputedAt": "2026-05-01T00:00:00Z"  },  "interpretationScale": {    "low": "Low risk — routine monitoring.",    "moderate": "Moderate risk — monitor and review at next visit.",    "elevated": "Elevated risk — consider proactive outreach.",    "high": "High risk — flag for prompt clinician review."  }}

Confidence and data quality are computed the same way everywhere: confidence blends input coverage with history depth, and completeness is the fraction of accepted inputs you actually supplied. That consistency means a single quality-gate in your code — if (intelligence.dataQuality.completeness < 0.5) ... — works across the entire suite.

Reasoning — explain: true

Pass explain: true in any intelligence request and the response's intelligence.reasoning field carries a plain-language interpretation of the deterministic findings. The interpretation is constrained to what the engine already computed — signals, thresholds, confidence, data-quality gaps, and change factors — and never adds diagnosis or treatment suggestions, so the deterministic posture is unchanged. Expect roughly 1–3 seconds of added latency; if generation fails or times out, the analysis still returns normally with reasoning: null.

intelligence.reasoning — present only when explain: true

FieldTypeDescription
narrativestring2–4 clinician-readable sentences interpreting the structured findings, including why the result changed when `previous` was supplied.
generatedAtstringISO timestamp of the interpretation.

Change tracking — why did the result move?

The API is stateless: we never store your clinical results. To get a change explanation, include the prior result as previous in any intelligence request. The response's intelligence.change block then explains the movement — score delta, status band transitions, and per-input comparisons.

previous — request field (all five endpoints)

FieldTypeDescription
previous.scorenumber?The prior numeric score, if the endpoint returns one.
previous.statusstring?The prior status/level string, exactly as returned before.
previous.computedAtstring?ISO timestamp of the prior computation. Echoed back in change.previousComputedAt.
previous.inputsobject?Prior input values keyed by field name. Enables per-input explanations like 'avgSleepHours improved from 5.1 to 7.5'.
request with previous snapshot
{  "assessments": [ ... ],  "avgSleepHours": 7.5,  "daysSinceLastVisit": 12,  "previous": {    "score": 44,    "status": "elevated",    "computedAt": "2026-05-01T00:00:00Z",    "inputs": { "avgSleepHours": 5.1, "daysSinceLastVisit": 30 }  }}

Explanations are deterministic and ordered by impact. If previous is omitted, change is null — nothing else in the response is affected.

Terminology codes — SNOMED CT, LOINC, RxNorm

Where a standardized clinical terminology exists for a concept in the response, the intelligence.codes array carries the mapping. Each entry names the code system, the code, a display string, and a mapsTo pointer to the response field it annotates — so you can chart results in an EHR without maintaining your own crosswalk.

Code systems in use

FieldTypeDescription
http://snomed.info/sctSNOMED CTClinical findings: MSE observations (e.g. 48767010 psychomotor agitation), risk states, medication response.
http://loinc.orgLOINCLab analytes on Biomarker Intelligence™ (e.g. 3016-3 TSH, 1989-3 Vitamin D) and instruments like 44261-6 PHQ-9.
http://www.nlm.nih.gov/research/umls/rxnormRxNormMedication ingredients on Medication Intelligence™ (e.g. 36437 sertraline).

Codes are additive metadata: they never change scoring, and unmapped concepts simply have no entry. Coverage grows over releases without a version bump.

Webhooks & event streaming

Long-running resources — lab orders and wearable connections — emit signed webhook events so you never poll. Configure endpoint URLs per environment in the developer dashboard; each endpoint gets its own signing secret (whsec_...).

Event types

FieldTypeDescription
lab_order.results_readyeventStructured results are parsed and available on the order.
lab_order.status_changedeventAny status transition (collected, in_transit, processing...).
wearable.connectedeventPatient completed the provider connect flow.
wearable.sync_completedeventA data sync finished; fresh metrics are queryable.
wearable.disconnectedeventPatient or provider revoked the connection.
event envelope
{  "id": "evt_7f3a91c24b8d4e0f92a1",  "object": "event",  "type": "lab_order.results_ready",  "apiVersion": "2026-07-01",  "createdAt": "2026-07-03T15:04:05.000Z",  "data": {    "object": "lab_order",    "id": "ord_x82k",    "status": "results_ready",    "patientRef": "pt_4491"  }}

Every delivery is signed with an Attunio-Signature header (HMAC-SHA256 over {timestamp}.{payload}). Verify the signature and reject stale timestamps before trusting any event:

verify.ts
import { createHmac, timingSafeEqual } from "node:crypto" export function verifyWebhook(payload: string, header: string, secret: string): boolean {  // Header format: t=<unix_ts>,v1=<hex_signature>  const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")))  const expected = createHmac("sha256", secret)    .update(`${parts.t}.${payload}`)    .digest("hex")  // Reject events older than 5 minutes to prevent replay.  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false  return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))}

Deliveries retry with exponential backoff for up to 24 hours until your endpoint returns a 2xx. Events are also queryable for 30 days via GET /v1/events for reconciliation, and you can stream them in order with the cursor parameter — useful for rebuilding state after downtime.

FHIR mappings & SMART on FHIR

Intelligence results map cleanly onto FHIR R4 Observation resources: the numeric score becomes valueInteger, summary.status drives interpretation, the headline lands in note, and entries from intelligence.codes populate code.coding — no local terminology work needed.

FHIR R4 Observation (from a relapse prediction)
{  "resourceType": "Observation",  "status": "final",  "category": [{    "coding": [{      "system": "http://terminology.hl7.org/CodeSystem/observation-category",      "code": "survey",      "display": "Survey"    }]  }],  "code": {    "coding": [{      "system": "http://snomed.info/sct",      "code": "225444004",      "display": "At risk for relapse"    }],    "text": "Attunio Relapse Prediction"  },  "subject": { "reference": "Patient/pt_4491" },  "effectiveDateTime": "2026-07-03T15:04:05Z",  "valueInteger": 38,  "interpretation": [{    "coding": [{      "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",      "code": "N",      "display": "Normal"    }],    "text": "moderate"  }],  "note": [{ "text": "Relapse risk 38/100 (moderate) — sleep restored, follow-up gap closed." }],  "device": { "display": "Attunio Relapse Prediction (deterministic_rules, engine default)" }}

Field mapping — unified schema → FHIR R4

FieldTypeDescription
summary.statusObservation.interpretationStatus band mapped to v3-ObservationInterpretation, original string kept in .text.
summary.headlineObservation.note[0].textThe human-readable one-liner, verbatim.
score / levelObservation.valueInteger | valueStringNumeric engines use valueInteger; categorical engines use valueString.
codes[]Observation.code.coding[]SNOMED/LOINC/RxNorm entries carried through with system + code + display.
provenance.generatedAtObservation.effectiveDateTimeWhen the intelligence was computed.
provenance.engineVersionObservation.device.displayEngine + version string for auditability.

Inside an EHR, use a standard SMART on FHIR launch: read charted assessments, score them through Attunio server-side, and write the Observation back to the chart.

SMART on FHIR launch (works in Epic/Cerner sandboxes)
import FHIR from "fhirclient"import { Attunio } from "@attunio/sdk" // 1. SMART on FHIR EHR launch (works in Epic, Cerner, Athena sandboxes)const fhirClient = await FHIR.oauth2.ready()const patient = await fhirClient.patient.read() // 2. Pull PHQ-9 scores already charted in the EHRconst bundle = await fhirClient.request(  `Observation?patient=${patient.id}&code=http://loinc.org|44261-6&_sort=date`)const assessments = bundle.entry.map((e: any) => ({  instrumentId: "phq9",  instrumentName: "PHQ-9",  score: e.resource.valueQuantity.value,  maxScore: 27,  createdAt: e.resource.effectiveDateTime,})) // 3. Score relapse risk with Attunio (server-side proxy holds the secret key)const client = new Attunio({ apiKey: process.env.ATTUNIO_API_KEY! })const prediction = await client.relapse.predict({ assessments }) // 4. Write the result back to the chart as a FHIR Observationawait fhirClient.create(toFhirObservation(prediction, patient.id))

End-to-end integration guide

The typical path from zero to production, distilled from real integrations. Most teams complete steps 1–4 in their first session.

1. Get keys

Create a developer account and grab a sandbox key (att_sk_test_...) from the dashboard. Sandbox is free and rate-limited; production keys require plan approval.

2. Health check

GET /v1/ping with your key. A 200 confirms auth, plan, and connectivity in one call.

3. First analysis

POST the telemetry you already have to the closest endpoint — most teams start with /v1/dmse/analyze or /v1/outcome/score. Read intelligence.summary first.

4. Wire change tracking

Store each response's score, status, and generatedAt. Pass them as `previous` on the next call and render intelligence.change.explanations in your UI.

5. Handle the edges

Branch on error.code (not HTTP status alone), log requestId, respect Retry-After on 429, and retry 5xx with backoff — or use an official SDK which does all four.

6. Go longitudinal

Add webhooks for lab results and wearable syncs, map outputs to FHIR Observations if you chart in an EHR, and enable more endpoints — the schema is already familiar.

Prefer working code over prose? Open the interactive explorer to run every endpoint with editable sample payloads, or start from a sample application.

Sample applications

Reference implementations demonstrating the three most common integration shapes. Each uses an official SDK, handles errors with request IDs, and renders the unified intelligence block.

Telehealth session sidebar

Next.js + TypeScript SDK

Captures session timing telemetry with @attunio/capture, streams it to /v1/dmse/analyze, and renders the intelligence.summary block live next to the video call.

Population risk dashboard

Python + FastAPI

Nightly batch job scores every active patient with /v1/relapse/predict, passes yesterday's result as `previous`, and surfaces the change.explanations feed to care managers.

EHR-embedded SMART app

SMART on FHIR + Java SDK

Launches inside the EHR, reads charted PHQ-9 Observations, scores outcomes with /v1/outcome/score, and writes results back as FHIR Observations with SNOMED codes.

Versioning & deprecation policy

The API is versioned at two levels, both designed so an integration you ship today keeps working without maintenance.

Versioning contract

FieldTypeDescription
URL version (/v1/)majorBreaking changes only. A new major version is a new URL prefix; /v1/ continues to run in parallel.
intelligence.schemastringThe unified block is versioned independently ("attunio.intelligence/v1"). Additive fields never bump it.
provenance.engineVersionstringScoring-engine calibration version on every response — pin results to the exact model that produced them.

What we will never do within /v1:

  • Remove or rename an existing response field
  • Change the type or meaning of an existing field
  • Add a new required request field
  • Repurpose an existing error code

What we may do without notice (additive):

  • Add new response fields, endpoints, event types, and terminology codes
  • Add new optional request fields
  • Improve engine calibration (always visible via provenance.engineVersion)

Deprecation windows: when a capability is scheduled for removal, it is announced in the changelog and response headers (Deprecation + Sunset, per RFC 8594) at least 12 months before sunset for GA endpoints and 3 months for beta endpoints. Deprecated-but-live endpoints keep full SLAs until their sunset date.

Performance — latency, uptime, rate limits

All intelligence endpoints are deterministic rule engines — no model inference in the request path — so latency is dominated by network transit and stays flat under load.

Service targets

FieldTypeDescription
Latency (p50)~40 msServer processing time for intelligence endpoints, excluding network transit.
Latency (p99)< 250 msIncludes cold paths and calibrated-model lookups.
Uptime SLA99.9%Production plans. Live status and 90-day history at the status page.
Payload limit1 MBPer request body. Batch large histories into the documented array caps.
Timeout30 sServer-side hard limit; requests never hang beyond it.

Rate limits by plan

FieldTypeDescription
Sandbox100 req/dayPer key, all endpoints pooled. Resets at 00:00 UTC. Free.
Launch10 req/s · 50k/moBurst to 20 req/s for 10 s. Overage billed per request.
Scale50 req/s · 500k/moBurst to 100 req/s. Dedicated support channel.
EnterpriseCustomCustom sustained/burst limits, private networking, BAA.

Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. On 429 you also get Retry-After in seconds — the official SDKs honor it automatically. Measure real-world latency yourself: every response's X-Request-Id pairs with the server timing so support can trace any slow call.

Errors

Errors return a non-2xx status and a consistent JSON envelope.

Error
{  "ok": false,  "requestId": "req_2ab90c37e51f4d6c80a1",  "error": {    "code": "invalid_request",    "message": "Telemetry validation failed.",    "docs": "https://attunio.com/developers/docs#errors",    "issues": [      { "path": "responseLatencySeconds", "message": "Number must be ≤ 120" }    ]  }}
StatusCodeMeaning
400invalid_requestBody is not valid JSON, or the payload failed schema validation.
401unauthorizedMissing or malformed Authorization header.
401invalid_tokenAPI key is invalid or has been revoked.
402payment_requiredA production key without an active paid plan and approved production access.
403forbiddenThe key is valid but not permitted to call this resource.
405method_not_allowedWrong HTTP method for the endpoint.
429rate_limitedDaily sandbox quota exceeded. Upgrade a plan to raise limits.
500server_errorUnexpected error processing the request.

Plans, rate limits & billing

Sandbox (test) keys are free and limited to 500 requests per UTC day per account. Exceeding the limit returns 429 rate_limited.

Production (live) keys require a paid plan and approved production access. Paid plans include a monthly call allowance and are never hard-blocked — calls beyond the allowance are billed as metered overage at the end of each cycle. A live key used without an active plan and approved access returns 402 payment_required.

Manage your plan, view real-time usage against your allowance, and request production access from the Billing tab of your developer dashboard. For custom or high-volume agreements, contact hello@attuniohealth.com.

Roadmap

All five intelligence endpoints, API-key authentication, self-serve subscription plans, and usage-based overage billing are live today. A full OAuth 2.0 client-credentials flow and webhook delivery of asynchronous results are next on the roadmap; the Bearer-token header contract above is forward-compatible with the OAuth bearer model, so no breaking change is expected when it ships.

Get your API keys