LiveDigital MSE™ API is now availableRead the docs
Back to API reference

First-party API

Retina Capture API

The session-authenticated API behind Attunio's investigational retinal screening. It powers the in-app patient capture flow and the native (Capacitor) shell — start a session, finalize a live frame or submit a fundus image, record quality, run investigational analysis, and read history. Only true fundus images are gradeable, and every result is decision support that a licensed clinician must review before it enters the chart.

On this page

Overview

The Retina Capture API is a first-party API — it is not part of the API-key–authenticated Clinical Intelligence suite. It exists to serve the Attunio patient app and the native capture shell, so it authenticates with the patient's own session rather than an att_sk_ key.

All endpoints are rooted at /api/retina/v1 and every read or write is scoped to the authenticated patient. A patient can only ever see or modify their own sessions, captures, and screenings.

Investigational.Retinal screening on Attunio is investigational and is not a diagnosis. Model output is decision support only and must be reviewed by a licensed clinician before it becomes part of a patient's record.

Authentication

Requests are authenticated with the patient's Better Auth session cookie — the same session used by the marketplace portal. There are no bearer tokens or API keys for this API. Requests without a valid patient session return 401.

The native (Capacitor) shell shares this contract: it reuses the authenticated web session, so the same endpoints back both the web capture flow and the native app without a second auth model.

Image types & gradeability

Accuracy depends entirely on what the image actually shows. The analysis model measures retinal vasculature and optic-disc morphology (an AutoMorph-style feature extraction feeding a gradient-boosted classifier), so it can only score images that contain the back of the eye.

image type

FieldTypeDescription
fundusgradeableA true retinal image — from a fundus camera or a phone clip-on ophthalmoscope lens. Eligible for morphology analysis and a model score.
anteriornot gradeableA bare-phone photo of the front of the eye (iris/pupil/sclera). Stored for record-keeping, but the model returns no retinal score — it cannot see the vasculature.
unknownnot gradeableProvenance could not be determined; treated conservatively as not gradeable.

The image type is derived from captureSource: clip_on_lens and fundus_upload fundus; a bare patient_capture anterior. When an image is not gradeable, the model produces no scores and instead records honest reasons — it never fabricates a retinal finding from a front-of-eye photo.

Where the score goes. Model scores, morphology features, SHAP-style feature contributions, and calibration metadata are clinician-only. They are written to the screening record for review in the clinician portal and are deliberately never returned by this patient-facing API — not from /capture, /fundus, /analyze, or /history.

Capture lifecycle

A capture moves a screening record through a small status ladder:

FieldTypeDescription
orderedstatusA session/screening exists but no image has been uploaded yet.
image_uploadedstatusThe best frame was uploaded, but analysis has not completed.
analyzedstatusThe model produced a result. Awaiting clinician review.
reviewedstatusA clinician accepted or rejected the result (terminal).

The typical flow is: POST /session POST /capture (which uploads, records quality, and runs analysis in one step) → clinician review. POST /quality and POST /analyze are supplementary — useful for the native shell or to retry analysis if the inline run failed.

Start a session — POST /session

Opens a capture session for the authenticated patient and returns its id. Pass the resolved device capabilities so the server records what the device could actually do (best-effort in the browser).

Body

FieldTypeDescription
targetEye"left" | "right" | "both"Which eye(s) the patient intends to capture. Defaults to "both".
deviceInfoobject?Optional device telemetry (userAgent, platform, screen) stored for QA and debugging.
captureCapabilitiesobject?Optional resolved CaptureBackend capabilities (torch, manualFocus, hdr, maxResolution) so the server records what the device could actually do.
POST /api/retina/v1/session
curl https://attuniohealth.com/api/retina/v1/session \  -X POST \  -H "Content-Type: application/json" \  --cookie "better-auth.session_token=<patient session>" \  -d '{    "targetEye": "both",    "deviceInfo": { "userAgent": "...", "platform": "iPhone" },    "captureCapabilities": {      "torch": false,      "manualFocus": false,      "hdr": false,      "maxResolution": { "width": 1920, "height": 1080 }    }  }'
200 OK
{  "sessionId": "rsess_9f2c41d07ab34e8a91b2"}

Get a session — GET /session/:id

Returns one session owned by the patient, including its linked screening (if the capture has been finalized).

GET /api/retina/v1/session/:id
curl https://attuniohealth.com/api/retina/v1/session/rsess_9f2c41d07ab34e8a91b2 \  --cookie "better-auth.session_token=<patient session>"
200 OK
{  "id": "rsess_9f2c41d07ab34e8a91b2",  "status": "completed",  "targetEye": "both",  "startedAt": "2026-07-03T15:02:11.000Z",  "completedAt": "2026-07-03T15:03:48.000Z",  "screening": {    "id": "rscr_7c1e58f2a90b4d3e62f4",    "status": "analyzed",    "statusLabel": "Awaiting clinician review",    "imageUrl": "https://blob.vercel-storage.com/retina/...jpg",    "imageEye": "both",    "reviewDecision": null,    "reviewedAt": null,    "createdAt": "2026-07-03T15:03:48.000Z"  }}

Finalize a capture — POST /capture

Uploads the accepted still (as multipart/form-data), records the capture and its quality, creates the investigational screening record, and runs the pluggable analysis model inline. Returns 201 with the new screening and capture ids. If analysis fails, the capture is still saved and the screening is left in image_uploaded so it can be re-analyzed.

This endpoint is for live device capture. Set captureSource to clip_on_lens when a retinal lens is attached (gradeable fundus image); otherwise it defaults to patient_capture (anterior, not gradeable — see Image types & gradeability). To submit an already-captured fundus image by URL, use POST /fundus instead.

multipart/form-data

FieldTypeDescription
imagefileThe accepted still frame as image/jpeg. Required.
sessionIdstringThe session started via POST /session. Required and must belong to the caller.
eye"left" | "right" | "both"Eye captured in this frame. Defaults to the session target.
method"auto" | "manual"Whether the auto-shutter or a manual tap produced the frame.
captureSource"patient_capture" | "clip_on_lens"How the frame was produced. "patient_capture" (default) is a bare-phone anterior image; "clip_on_lens" produces a gradeable fundus image. Determines the image type and whether the morphology model can score it.
widthintegerPixel width of the captured frame.
heightintegerPixel height of the captured frame.
qualityjson string?Optional serialized QualityAssessment for the frame (see the quality object below).
POST /api/retina/v1/capture
curl https://attuniohealth.com/api/retina/v1/capture \  -X POST \  --cookie "better-auth.session_token=<patient session>" \  -F "sessionId=rsess_9f2c41d07ab34e8a91b2" \  -F "eye=both" \  -F "method=auto" \  -F "width=1920" \  -F "height=1080" \  -F 'quality={"overallScore":0.86,"grade":"good","sharpness":0.9,"exposure":0.8,"contrast":0.82,"fieldCoverage":0.77,"glareScore":0.05,"motionScore":0.04,"passed":true,"issues":[]}' \  -F "image=@best-frame.jpg;type=image/jpeg"
201 Created
{  "screeningId": "rscr_7c1e58f2a90b4d3e62f4",  "captureId": "rcap_2ab90c37e51f4d6c80a1"}

Submit a fundus image — POST /fundus

Creates an investigational screening from an already-uploaded fundus image — from a fundus camera or a phone clip-on retinal lens. Because the image contains the retina, this path runs the full morphology analysis inline and produces a gradeable result (still pending clinician review). Upload the file first (e.g. via the client Blob upload), then pass its URL here. Returns 201; captureId is empty because there is no live capture session for an uploaded image.

Body

FieldTypeDescription
imageUrlstring (url)Public URL of an already-uploaded fundus image (e.g. a Blob url from the client upload).
eye"left" | "right" | "both"Eye captured in the image. Defaults to "both".
captureSource"fundus_upload" | "clip_on_lens"Provenance of the fundus image. Both are gradeable; defaults to "fundus_upload".
POST /api/retina/v1/fundus
curl https://attuniohealth.com/api/retina/v1/fundus \  -X POST \  -H "Content-Type: application/json" \  --cookie "better-auth.session_token=<patient session>" \  -d '{    "imageUrl": "https://blob.vercel-storage.com/retina/fundus-abc.jpg",    "eye": "both",    "captureSource": "fundus_upload"  }'
201 Created
{  "screeningId": "rscr_a1b2c3d4e5f60718293a",  "captureId": ""}

Record quality — POST /quality

Persists a standalone quality assessment for a capture. The web flow already embeds quality in the capture call; this endpoint exists for the native shell when quality is graded in a separate step. The capture must belong to the caller.

POST /api/retina/v1/quality
curl https://attuniohealth.com/api/retina/v1/quality \  -X POST \  -H "Content-Type: application/json" \  --cookie "better-auth.session_token=<patient session>" \  -d '{    "captureId": "rcap_2ab90c37e51f4d6c80a1",    "quality": {      "overallScore": 0.86,      "grade": "good",      "sharpness": 0.9,      "exposure": 0.8,      "contrast": 0.82,      "fieldCoverage": 0.77,      "glareScore": 0.05,      "motionScore": 0.04,      "passed": true,      "issues": []    }  }'
200 OK
{  "qualityAssessmentId": "rqa_5d6e7f8091a2b3c4"}

Run analysis — POST /analyze

Runs (or re-runs) the pluggable analysis model on a patient-owned screening and writes the result. Useful after a /capture whose inline analysis failed. The result lands in analyzed awaiting clinician review — scores are never surfaced to the patient here.

POST /api/retina/v1/analyze
curl https://attuniohealth.com/api/retina/v1/analyze \  -X POST \  -H "Content-Type: application/json" \  --cookie "better-auth.session_token=<patient session>" \  -d '{ "screeningId": "rscr_7c1e58f2a90b4d3e62f4" }'
200 OK
{  "screeningId": "rscr_7c1e58f2a90b4d3e62f4",  "status": "analyzed",  "ran": true}

Screening history — GET /history

Lists the authenticated patient's screenings, newest first.

GET /api/retina/v1/history
curl https://attuniohealth.com/api/retina/v1/history \  --cookie "better-auth.session_token=<patient session>"
200 OK
{  "screenings": [    {      "id": "rscr_7c1e58f2a90b4d3e62f4",      "status": "reviewed",      "statusLabel": "Reviewed by your clinician",      "imageUrl": "https://blob.vercel-storage.com/retina/...jpg",      "imageEye": "both",      "reviewDecision": "accepted",      "reviewedAt": "2026-07-04T14:20:00.000Z",      "createdAt": "2026-07-03T15:03:48.000Z"    }  ]}

Partner ingestion — POST /api/v1/retina/ingest

The server-to-server path for clinical-grade inputs. A partner clinic — or a portable fundus device on the partner network — submits a fundus image for one of our patients. This is how we bring in real fundus-camera data, distinct from the in-app patient capture above.

Different auth. Unlike the endpoints above (patient session cookie), this endpoint lives on the developer API and authenticates with a secret Bearer key (Authorization: Bearer att_sk_...). Publishable keys are rejected. It shares the developer API's quota, billing, and usage logging.

Clinician-only output. The response returns a safe summary only — screeningId, status, and gradeability. Model scores, morphology, SHAP, and calibration are stored for the clinician portal and are never returned to the partner. Every ingested screening is pending clinician review.

Body

FieldTypeDescription
patientRefstringAttunio patient user id the image belongs to. The clinic-to-patient mapping is configured per partner.
imageUrlstring (url)?Partner-hosted URL of the fundus image; we fetch and copy it into our own store. Provide this or imageBase64.
imageBase64string?Inline base64 image (a data: URL is accepted). Alternative to imageUrl.
eye"left" | "right" | "both"Eye captured. Defaults to "both".
source"partner_network" | "in_clinic" | "portable_device"Provenance of the capture. All are approved fundus sources. Defaults to "partner_network".
imageType"fundus" | "anterior"Honesty override. Defaults to fundus for a partner source; set "anterior" for a front-of-eye image (stays not gradeable).
deviceModelstring?Fundus camera / device model, stored for provenance.
externalIdstring?Partner's own record id / MRN, stored for reconciliation.
indicationstring?Clinical reason for the screen.
patientAgeYearsinteger?Patient age, if the model conditions on it.
POST /api/v1/retina/ingest
curl https://attuniohealth.com/api/v1/retina/ingest \  -X POST \  -H "Authorization: Bearer att_sk_live_..." \  -H "Content-Type: application/json" \  -d '{    "patientRef": "usr_8f2a...",    "imageUrl": "https://clinic.example.com/exports/fundus-9021.png",    "eye": "both",    "source": "in_clinic",    "deviceModel": "Topcon NW400",    "externalId": "MRN-55213"  }'
201 Created
{  "ok": true,  "requestId": "req_9f2b7c1d0a3e4f5g6h7i",  "data": {    "object": "retina.ingestion",    "screeningId": "rscr_a1b2c3d4e5",    "status": "analyzed",    "imageType": "fundus",    "gradeable": true,    "gradeabilityReasons": [],    "requiresClinicianReview": true,    "analysisRan": true,    "disclaimer": "Investigational, clinician-reviewed decision support — not a diagnosis. ..."  }}

Objects

The quality object attached to a capture and the screening object returned by reads:

quality

FieldTypeDescription
overallScorenumberComposite 0–1 quality score for the frame.
grade"excellent" | "good" | "acceptable" | "poor"Human-readable grade derived from the score.
sharpnessnumber0–1 focus/sharpness metric (Laplacian variance, normalized).
exposurenumber0–1 exposure adequacy.
contrastnumber0–1 contrast metric.
fieldCoveragenumber0–1 estimate of how much of the target field is filled.
glareScorenumber0–1 glare presence (lower is better).
motionScorenumber0–1 motion blur estimate (lower is better).
passedbooleanWhether the frame cleared the quality gate.
issuesstring[]Human-readable reasons a frame failed, e.g. ["too dark", "glare detected"].

screening

FieldTypeDescription
idstringScreening id (rscr_…). The clinical record that clinicians review.
statusstringLifecycle status — see the status ladder below.
statusLabelstringPatient-friendly label for the status.
imageUrlstring | nullURL of the captured fundus image.
imageEyestring | nullEye associated with the image.
reviewDecision"accepted" | "rejected" | nullThe clinician's decision, once reviewed.
reviewedAtISO date | nullWhen a clinician completed review.
createdAtISO dateWhen the screening record was created.

Errors

Errors return a non-2xx status with a JSON body of the shape { "error": string }. Ownership failures return 403so patients can never probe others' records.

StatusMeaning
400Bad request — missing image, missing sessionId, or a body that failed validation.
401Unauthorized — no valid patient session cookie.
403Forbidden — the session/capture/screening does not belong to the caller.
404Not found — the requested session id does not exist for this patient.
500Server error — unexpected failure finalizing or analyzing.
403 Forbidden
{  "error": "Forbidden"}