Casebook

What is stored, exactly

The short version: scores, ages, dates of testing, controlled terms you choose from a list, and one direct identifier the practice chooses to keep — a date of birth. No name, and nothing else that names anybody.

One identifier column, and it is a choice

A case carries a date of birth, and it is the only direct identifier Casebook stores. That is a deliberate decision by the practice, and it makes this a system that holds protected health information — kept, encrypted, logged and covered by the agreement described below, rather than a system that holds none.

Everything else that would name a person still has no column at all: no name, no medical record number, no email, no telephone number, no address, in any table that holds patient data. That is not a policy either — it is the schema, and a test walks every table and fails the build if any of those names appears. The date of birth is the single, named exception to that test; every other identifier stays forbidden on every table.

A person is otherwise a pseudonymous code you assign, like PT-0412. The mapping from that code to a name lives in your practice; Casebook has no column for a name and no endpoint that could return one.

Date of birth, and the age computed from it

When you enter a date of birth on a case, it is stored, and the age at each testing is computed from it rather than re-typed — one source of truth for a value that anchors every norm. It is shown in full at every age — a plain number, not a normalized one.

The date of birth has exactly one home: its column on the case, filled only when you type it there. It is never scraped from an uploaded report — the parser still refuses to extract a date of birth even when the report prints it — and it is never allowed into a free-text field, where a date in prose is refused at the point of writing. It goes where you put it and nowhere else.

An uploaded report is read once and dropped

A finished neuropsychological report carries the name, the date of birth, the referral letter and often the parents’ contact details. Casebook extracts its text for that one request, parses it, shows you the rows it proposes, and keeps none of it. There is no column for the source text, no cache and no copy on disk. Keeping it “for auditing” would undo the entire design in one field.

The two free-text fields, and the honest limit

A case summary and a note on a score are free text, and free text is where a name eventually gets typed. Two things happen there and they are deliberately different.

Text carrying an identifier with a recognizable shape — an email address, a telephone or fax number, a social security number, a record number, a web address, an IP address, a full calendar date — is refused. Not warned about: refused, at the point of writing.

Text carrying a name-shaped phrase is saved and flagged. No detector can decide whether “Grace” is a child or a quality, and half the instruments in the catalog are named after people — a check strict enough to catch every first name would also refuse “Rey Complex Figure”, and a check people route around protects nothing. So it tells you while the sentence is still on screen.

And it can be audited afterwards: one request sweeps every free-text field in your database and reports how many carry what, and exactly where. It returns locations and kinds, never the text — a report of where the identifiers are is not much use if reading it copies them somewhere new.

One practice cannot see another

Every case and every score belongs to a practice. The filter that enforces it is not written into each query — it is attached to the database session, and applied to every statement that touches a table with an account on it, including the relationship loads that a hand-written filter misses.

A query with no practice in scope raises an error rather than returning everything. The failure mode of a forgotten filter is a five-hundred on the first request in development, not a disclosure in production. The handful of operations that legitimately span practices — signing in, creating an account — each say so in one line that can be found in a single search.

Who did what

Every case opened, changed, deleted or exported is recorded with who, when and which code. It is readable by everyone in your practice rather than by owners only: an access log that only the person most likely to be asked about it can read is a weaker control than one everybody can see.

Signing in is refused after eight failed attempts against one address in fifteen minutes, counted in the database rather than in memory so it holds across servers. A failed attempt is recorded against a short hash of the address and never the address itself — a wrong guess is often a typo or somebody else’s, and writing it into a readable log turns the guess into a record of who was guessed at.

Leaving

Export the whole knowledge base whenever you like: a zip of CSVs with a codebook describing every column. Closing your practice deletes its people, every case, every score and the log — the log especially, because kept after the cases have gone it is a list of code names and the times somebody read them.

Where it runs

AWS, encrypted at rest and in transit, in a private network with no route to the internet from the database. Thirty days of backups.

It runs on HIPAA-eligible AWS services under a business-associate agreement with AWS. Because Casebook stores a date of birth, it holds protected health information, and a business-associate agreement with us is the appropriate arrangement — we will sign one. Ask, and we will send it.

Before you put real records in anything, including this: satisfy yourself about it. Ask for the schema — we will show you. Ask what happens to an uploaded report. Ask what leaves the system and in what form. A vendor who cannot answer those in specifics is telling you something.

Questions a compliance review will ask

Send them. Detailed answers about the schema, the retention, the isolation and the export format are the easy part — the whole system was built so that they would be.