Digits Health — Clinical API Documentation
The Clinical API is the Fastify 5 service (apps/api) that backs the Digits Health hand
rehabilitation platform. It persists assessments, patient records, journals, reports, and
subscriptions to PostgreSQL (via Prisma), proxies camera-based biomechanics analysis to a
Python service, and exposes a real-time AI voice orchestrator over WebSocket.
This folder is the source of truth for integrating with the API.
Contents
| Document | What's inside |
|---|---|
| Getting started | Base URLs, environments, your first request, conventions. |
| Authentication | Auth0 RS256 JWTs, roles, optional vs. required auth, dev mode. |
| API reference | Every endpoint, grouped by route, with request/response shapes. |
| Clinical concepts | Assessment types, scoring, enums, the result-snapshot model, codes. |
| Errors | Error envelope, status codes, and handling guidance. |
| Webhooks | Stripe billing webhook events and verification. |
| OpenAPI spec | Machine-readable OpenAPI 3.1 definition for codegen / Postman / Swagger. |
SDK
A first-party, fully-typed TypeScript client is available at
packages/clinical-sdk (@digits/clinical-sdk).
Prefer it over hand-rolled fetch calls — it handles auth, retries, timeouts, and typed
errors for you.
import { DigitsClient } from '@digits/clinical-sdk';
const digits = new DigitsClient({
baseUrl: 'https://api.digitshealth.com/api',
token: () => getAccessToken(),
});
const rom = await digits.assessments.submitRom({ hand: 'RIGHT', overallScore: 82 });
Architecture at a glance
Browser ── Next.js BFF proxy ──► Clinical API (Fastify, :3001) ──► PostgreSQL (Prisma)
│
├─► Python biomechanics service (HTTP)
├─► Google Cloud TTS / Gemini (AI)
└─► Stripe (billing)
The web app never calls the API directly from the browser — it forwards through a BFF proxy that injects the Auth0 access token. External/native integrations call the API directly with their own bearer token. See Authentication.
Route groups
All routes are mounted under /api:
| Prefix | Group | Auth |
|---|---|---|
/api/health | Health probe | none |
/api/assessments | Submit & fetch ROM / dexterity / pain / arthritis | optional |
/api/lookup | Public lookup, claim, email, anonymous create (by code) | mixed |
/api/patients | Score/assessment history, profiles, journal, narrative | required |
/api/journal | Hand-journal check-ins | none* |
/api/reports | Bundled reports, PDF, email, AI explanation | optional |
/api/biomechanics | Wrist/thumb kinematic analysis (proxied) | none |
/api/ai | Text-to-speech (REST) + orchestrator (/api/ai/ws, WebSocket) | none |
/api/billing | Stripe checkout, portal, subscription, webhook | required** |
/api/leads | Developer/partner sign-up (marketing site) | none |
* Journal endpoints are not currently behind an auth guard but expect a patientId.
** The webhook (/api/billing/webhook) is server-to-server and uses Stripe signature
verification rather than a JWT.
Conventions
- JSON in and out (except PDF download and TTS audio).
Content-Type: application/json. - BigInt IDs in the database are serialized to JSON numbers in responses.
- 6-character codes (
A–Z/2–9, ambiguous chars removed) identify shareable assessment records and reports; accepted with or without the middle dash (XHE7JR/XHE-7JR). - Snake_case appears in some camera-derived payloads (e.g. wrist
max_extension_deg); everything else is camelCase.