Clinical concepts
This document explains the domain model behind the API: assessment types, how scores work, the result-snapshot pattern, the shareable code system, and the controlled vocabularies (enums) you'll send and receive.
Disclaimer. The platform measures digital biomarkers of hand function. Outputs such as the arthritis pattern/progression are not a medical diagnosis. The canonical patient-facing disclaimer is exported from
@digits/sharedasARTHRITIS_DISCLAIMER.
Assessment types
| Type | Endpoint | What it measures |
|---|---|---|
| ROM | POST /assessments/rom | Range of motion of the wrist and finger joints (degrees) via camera + MediaPipe. |
| Dexterity | POST /assessments/dexterity | Finger-tapping speed, accuracy, and consistency. Persisted internally as FINGER_TAPPING. |
| Pain | POST /assessments/pain-v2 | Self-reported pain by hand location, plus medications and management strategies. |
| Arthritis | POST /assessments/arthritis | Composite arthritis-risk evaluation from a questionnaire + joint symptoms + (optionally) ROM/pain sources. |
The stored
assessmentTypefor dexterity isFINGER_TAPPING; read endpoints normalize it toDEXTERITYfor clients. SimilarlyTHUMB_MOVEMENTSnormalizes toTHUMB.
Where the scoring happens
ROM and dexterity raw kinematics are computed from camera landmarks. The biomechanics
endpoints proxy frame data to a Python service for angle extraction; the assessment
submission endpoints persist the already-scored results. Arthritis composite scoring is
performed client-side and submitted whole — the API stores the breakdown but does not
recompute it. Shared scoring helpers live in @digits/shared (scoring/handScore.ts).
Scores
- Most scores are 0–100, higher is better (
overallScore,wristScore,fingerScore,speedScore,consistencyScore,touchPrecision, arthritis sub-scores,compositeScore,healthScore). - Pain severity is 0–10, higher is worse (per pain level labels).
- ROM wrist angles are in degrees. Reference ranges used by the API when scoring wrist
sub-joints: extension
0–70°, flexion0–80°. - Trend (provider list) is derived by comparing the two most recent daily scores:
> +2→improving,< −2→declining, elsestable. A patientneedsAttentionwhen declining or when the latest score< 40.
The result-snapshot pattern
Every shareable assessment creates an AssessmentRecord carrying a resultSnapshot — a
denormalized JSON blob with the headline numbers for that assessment type. This is what
GET /lookup/:code and report bundling read, so results render without loading the full
child tables. GET /assessments/:id/results additionally returns the full normalized
results tree (hand/joint/finger rows).
Typical snapshot keys by type:
| Type | Snapshot keys |
|---|---|
| ROM | overallScore, wristScore, fingerScore, extensionDeg, flexionDeg, lightingQuality |
| Dexterity | overallScore, speedScore, touchPrecision/accuracyScore, consistencyScore, cycleCount, totalTouches, sequencesCompleted, timeTo10Sequences, averageFrequency, sequenceAccuracy |
| Pain | overallScore (derived 100·(1 − maxSeverity/10)), mode, maxSeverity, painType, timeOfDay, entries[], strategies[], medications[], comments |
| Arthritis | compositeScore, progressionLevel, patternType, romDeficitScore, painBurdenScore, functionalScore, stiffnessScore, affectedJointCount, clinicalFlags[] |
Shareable codes
Every assessment and report gets a 6-character code drawn from
ABCDEFGHJKMNPQRSTUVWXYZ23456789 — ambiguous characters (0/O, 1/I/L) are excluded so
codes are easy to read aloud and type. Codes are:
- Case-insensitive and accepted with or without the middle dash:
XHE7JR≡xhe-7jr≡XHE-7JR.formattedCodeis the dashed form for display. - Expiring after 90 days when anonymous; permanent once claimed by an authenticated user.
- The lookup key for public retrieval, claiming, emailing, and report bundling.
Lighting quality
Camera assessments record a three-state lightingQuality (GOOD / FAIR / POOR) plus
optional lightingMetrics (meanY, stdY) so results can be interpreted in context of
capture conditions.
Enums
These controlled vocabularies are defined as Zod enums in @digits/shared and re-exported
by @digits/clinical-sdk. Send the enum value (the uppercase token), not the label.
Hand / digits / locations
- Hand:
LEFT,RIGHT(ROM/dexterity);BOTHis also valid for pain/arthritis. - HandDigit:
THUMB,INDEX,MIDDLE,RING,LITTLE. - DigitLocation:
TIP,DIP,PIP,MCP,CMC,PALM,DORSAL.
Pain
- PainType:
SHARP,DULL_ACHING,BURNING,THROBBING,TINGLING_NUMBNESS,STIFFNESS,SWELLING. - PainTimeOfDay:
MORNING,AFTERNOON,EVENING,NIGHT,CONSTANT. - PainAssessmentMode:
QUICK,DETAILED. - PainAreaType:
DIGIT_JOINT,PALM,DORSAL,WRIST. - ManagementStrategy:
ELEVATION,REST,ICE,COMPRESSION,SPLINTING,OTHER. - Pain severity (0–10): 0 No pain · 1 Minimal · 2 Mild · 3 Uncomfortable · 4 Moderate · 5 Distracting · 6 Distressing · 7 Unmanageable · 8 Intense · 9 Severe · 10 Worst possible.
Arthritis
- MorningStiffnessDuration:
NONE,UNDER_15_MIN,MIN_15_TO_30,MIN_30_TO_60,OVER_60_MIN. - DifficultyLevel:
NONE,MILD,MODERATE,SEVERE,UNABLE(scored 100/75/50/25/0). - FunctionalTask:
GRIPPING,PINCHING,TURNING_KEY,OPENING_JAR,WRITING,BUTTONING,CUTTING_FOOD,CARRYING,WRINGING,TYPING. - SymptomOnset:
GRADUAL_MONTHS,GRADUAL_YEARS,SUDDEN_WEEKS,FLARE_UPS. - SymptomSymmetry:
LEFT_ONLY,RIGHT_ONLY,BOTH_SYMMETRIC,BOTH_ASYMMETRIC. - ArthritisPattern:
UNDETERMINED,OA_PATTERN,RA_PATTERN,MIXED. - Progression level (0–4): 0 No Concerns · 1 Minimal · 2 Moderate · 3 Significant · 4 Severe.
Journal
- entryType:
PRE_ASSESSMENT,POST_ASSESSMENT,AD_HOC_CHECK_IN,INITIAL_INTAKE.
Data domains (database)
Backed by PostgreSQL via Prisma (apps/api/prisma/schema.prisma, ~60 tables, BigInt PKs):
- Identity —
users,user_roles(Auth0 link viaauth0Sub),contacts(patients). - Assessments —
rom_assessment_result,dexterity_assessment_result,pain_assessment_session,arthritis_evaluation, each with hand/joint/finger child rows. - Records —
assessment_records(the shareable code + snapshot layer). - Reports —
reports(bundled, with view counts and AI explanation). - Journal —
hand_journal_entries. - Billing —
subscriptions(Stripe-backed, tiersCORE/PLUS).