Data flows
The architecture in one sentence
MedJot is local-first: clinical data is created, stored and used on the device, and the server exists for authentication, licensing, support and optional AI — not for clinical storage.
What never leaves the device
Held in browser local storage and an on-device IndexedDB database:
- Patient findings and examination results
- Observation sets, vital signs and NEWS2 scores
- Free-text notes in every section
- The generated report and any manual edits
- The clinician notepad, including sketches
- Scores, care bundles, frailty assessments
- Ingestions and patient weight
Autosave writes to the device only. There is no upload, no server-side backup and no sync of report content between devices. We hold no copy and have no mechanism to retrieve one.
What crosses the network, and when
| Data | When | Destination |
|---|---|---|
| Authentication and refresh tokens | Sign-in, and on session refresh | Google Firebase |
| Subscription and licensing status | On load and periodically | MedJot API |
| Usage counters — a count and a date | On starting a report | MedJot API |
| Account profile and consent answers | When set or changed | MedJot API |
| Redacted clinical summary | Only when a clinician runs an AI feature | MedJot API → Google Gemini |
| Report content, in memory for ≤10 min | Only on an explicit device transfer | MedJot API |
| Saved templates | When a user saves one | MedJot API |
| Support enquiries | When sent | MedJot API |
| Product analytics events | Only after explicit opt-in | MedJot API + Google Analytics |
| Payment details | At checkout | Stripe directly — never through us |
The AI path in detail
This is the main route by which clinical content leaves a device, so it warrants precision:
- Triggered only by a clinician pressing the button on an AI feature. Nothing happens in the background.
- The request is assembled from structured application state, not from the report text on screen — meaning the content sent is an enumerable set of fields rather than arbitrary screen content.
- Redaction runs on both the client and the server before transmission: names introduced by a relationship word, NHS numbers, phone numbers, postcodes and other structured identifiers are stripped. The patient is referred to only as "Pt".
- Neither the request text nor the response is stored. We log the feature name, token counts and cost — for billing and abuse prevention — and nothing else.
Redaction is a best-effort heuristic, not a guarantee. It cannot reliably catch a name typed into free text with no surrounding cue. This is why the product's standing instruction to clinicians is to keep patient-identifiable information out of MedJot entirely — see Privacy and governance.
The device transfer path
Report content is sent to our server, held in memory only — never written to disk or a database — under a one-time code for a maximum of 10 minutes, then deleted automatically. Users can delete it sooner. See Device transfer.
What we hold about a user
Account record: name, email, phone, month and year of birth, country and county, role and sector, account creation and last sign-in dates, Premium status and Stripe customer reference, trial usage, daily report count and date, saved templates, consent answers, and a small set of hashed anti-fraud signals.
Not held: day of birth, employer, trust, station, professional registration number, address, town or postcode — none of which is collected. And no clinical content of any kind.
Anti-fraud signals
A deliberately short list: a random browser-generated device identifier stored as a one-way hash; a salted one-way hash of the IP address — the address itself is not stored; a normalised form of the email address; and flags for automatically-generated-looking addresses or names.
No browser fingerprinting, no tracking pixels, no third-party trackers. Retained 12 months from last sign-in.
Retention
| Data | Retention |
|---|---|
| Clinical data on the device | Until the user clears it. Not ours. |
| Device transfer payloads | 10 minutes, in memory, then deleted |
| Account, billing and template data | Life of the account, or until deletion is requested |
| AI usage logs — feature, tokens, cost; never content | Currently indefinite, for billing and abuse prevention |
| Detailed analytics events (opt-in only) | Up to 90 days, or until opt-out |
| Anonymous daily analytics totals | Indefinite — no identifier, not linked to anyone |
| Anti-fraud device and IP hashes | 12 months from last sign-in |
| Support enquiries | As long as needed to administer them |
Analytics, if you are assessing it
Off unless the user opts in. Events come from a closed catalogue: every parameter is an enum, a boolean or a bounded integer. There is no free-text field anywhere in the system — not filtered, but structurally absent. The server revalidates every event against that catalogue and discards anything unrecognised.
Attribution — role, sector, region, entitlement, organisation, platform — is added server-side from the account record; the browser never sends it, so it cannot be spoofed.
No account identifier is ever sent to Google. Advertising features, Google Signals and ad personalisation are all disabled, and IP is anonymised. If the user has not opted in, the Google analytics code is never loaded.
Opting out purges that user's detailed event log.
Data residency
Our infrastructure is UK-hosted. Google (Firebase, Gemini) and Stripe may process data outside the UK and EEA as part of their own global infrastructure, under their respective data protection commitments. See Subprocessors.
The consequence for inspection policy
Because there is no clinical content in transit, decrypting MedJot yields no clinical visibility — while placing authentication and refresh tokens in front of the inspecting appliance. See TLS inspection.