Data model
What is stored, in what shape, and why. Useful if you are reasoning about the platform from the outside — or if you are an agent trying to understand what a tool result actually represents.
The shape of a day
The atomic unit is one user, one date:
{
userId: "…",
dateKey: "2026-08-01", // YYYY-MM-DD, the natural key
date: ISODate("…"),
data: {
wakeAt7am: true,
takeVitamins: true,
weight: 178.4,
pushUps: 60,
breakfast: "eggs, oats",
whoopRecovery: 71,
whoopHrv: 88.2,
// …any custom column, keyed by its id
},
lastUpdated: "…",
// Retrieval
embedding: [ /* vector */ ],
embeddingText: "…", // the human-readable text that was embedded
embeddedAt: ISODate("…"),
needsEmbedding: false,
embeddingStatus: "embedded"
}
Three things are worth drawing out.
Keys inside data are canonical metric ids. whoopHrv, not "HRV". They come from the metric registry, which is the same source of truth the API, the correlation engine and the AI tools read. WHOOP fields sit in the same object as fields you typed — there is no separate wearable record to join.
Custom columns live in the same object. A custom column is not a second-class citizen stored elsewhere; it is a key in data with an entry in your column settings describing it.
The embedding lives on the document. Each day carries its own vector and the exact text that produced it, so retrieval and the audit of what was retrieved are the same object.
Collections
| Collection | Holds |
|---|---|
routineData |
Tracked days, one document per user per date, with embeddings |
routineSummaries |
Narrative rollups: weekly, monthly, quarterly, yearly, lab and methylation interpretations, mission analysis, correlation narratives |
users |
Accounts, hashed passwords, and the profile — sensitive fields encrypted |
userLearnings |
Insights extracted from conversation, embedded and scored |
columnSettings |
Column definitions, order, visibility, custom column metadata |
columnWidths |
Per-column display widths |
dailyFeatures |
The feature store — normalized values and z-scores per day |
apiTokens |
Hashed machine credentials with scopes and revocation state |
labResults |
Bloodwork panels and extracted markers |
methylationResults |
The one-time genetic profile |
Derived artifacts
Several things are computed rather than entered, and stored so they can be read cheaply.
Feature store. Every metric normalized to a number plus its z-score against a trailing 30-day baseline. It is what the correlation engine reads and what get_daily_features returns. Stamped with the registry version, so a change to a metric's definition can trigger a targeted rebuild rather than a silent stale read.
Correlation edges. Relationships as graph edges with coefficient, partial coefficient, lag, p-value, sample size, tier and provenance. See analytics.
User facts. All-time aggregates per metric — extremes, totals, streaks, rolling averages — derived from the registry rather than a hand-maintained field list.
Insights. The precomputed statistical models: readiness, goal ETA, changepoints, dose-response, and the rest.
Summaries. Narrative rollups, embedded alongside days so retrieval can find a period as a unit.
What is encrypted
Sensitive profile fields in users are encrypted with AES-256-GCM. Ledger data is not. The reasoning is in security and privacy.
Retrieval indexes
Alongside conventional indexes on userId and dateKey, the database maintains a vector index over day and summary embeddings and full-text indexes over the same content. A question runs both, the results are fused, and the survivors are reranked. Neither alone is sufficient: vector search misses exact terms, text search misses paraphrase.
Consistency
The database is the single source of truth. The browser keeps essentially no cache of your data, every edit is written immediately, and every read comes from the database.
Server-side caching exists for expensive computations, with a bounded TTL, and is invalidated for your account whenever you write. So a figure the coach quotes is either fresh or was computed against the full history by a background worker — never a stale value from a browser that has been open since yesterday.
Reading it from outside
The MCP server exposes most of this read-only. get_day_details returns a single day, get_daily_features the feature matrix, get_correlation_edges the graph, get_user_facts the aggregates, get_insights the models, and get_summaries the narratives. Full schemas in the tool reference.
Not exposed: raw account records, password or token hashes, and progress pictures.
Related
- Metric reference — every field in
data - Security and privacy — encryption and access
- Analytics — how the derived artifacts are produced