fix(backup): carry the import ledger, and the keys of deleted imports

The backup carried neither the import ledger nor the tombstones of
deleted documents, so after a restore onto a fresh instance a token
importer stored every imported document the person had deleted on
purpose again.

The document filing section now carries documentImportKeys: the
ledger's rows plus the own and alias keys of the documents waiting in
Recently deleted, which still do not travel as documents. The restore
writes them back, minus any key a restored live document holds, and
counts them in the restore summary and the preview. DocumentImportKey
moves from the excluded models to the two-ended ones, and the round
trip, the column audit and the parity goldens carry a ledger row.
This commit is contained in:
Marc-André Bombeck committed 2026-10-25 02:42:09 +01:00
1 parent 988af10a34
commit 205d8f257c
25 files changed
+906 -348

No files matched your search

+29
View File
@@ -44015,6 +44015,34 @@ components:
- createdAt
- updatedAt
additionalProperties: {}
documentImportKeys:
default: []
type: array
items:
type: object
properties:
sourceSystem:
type: string
enum:
- PAPERLESS
- PAPRA
- OTHER
sourceId:
type: string
minLength: 1
maxLength: 128
sourceInstance:
anyOf:
- type: string
maxLength: 512
- type: "null"
createdAt:
type: string
minLength: 1
required:
- sourceSystem
- sourceId
additionalProperties: {}
nutrientDays:
default: []
type: array
@@ -46536,6 +46564,7 @@ components:
- documents
- documentConditionLinks
- extractedFacts
- documentImportKeys
- nutrientDays
- healthProfile
- healthProfileFacts
@@ -75,6 +75,7 @@ vi.mock("@/lib/db", () => ({
// Empty for the same reason as the sections above.
documentConditionLink: { findMany: vi.fn().mockResolvedValue([]) },
extractedFact: { findMany: vi.fn().mockResolvedValue([]) },
documentImportKey: { findMany: vi.fn(async () => []) },
ecgRecording: { findMany: vi.fn().mockResolvedValue([]) },
symptomDefinition: { findMany: vi.fn().mockResolvedValue([]) },
lifeEvent: { findMany: vi.fn().mockResolvedValue([]) },
@@ -76,6 +76,7 @@ vi.mock("@/lib/db", () => ({
// What a document was filed against, and what was read out of it.
documentConditionLink: { findMany: vi.fn() },
extractedFact: { findMany: vi.fn() },
documentImportKey: { findMany: vi.fn(async () => []) },
ecgRecording: { findMany: vi.fn() },
symptomDefinition: { findMany: vi.fn() },
lifeEvent: { findMany: vi.fn() },
@@ -305,6 +306,24 @@ describe("v1.4.41 W-DELETED-2 — soft-delete invisibility", () => {
const documentCall = vi.mocked(prisma.inboundDocument.findMany).mock
.calls[0][0] as { select?: Record<string, unknown> };
expect(documentCall.select).not.toHaveProperty("contentEncrypted");
// The import ledger reads the source keys of documents waiting in
// Recently deleted, and nothing else of them: no tombstone travels.
for (const [args] of vi.mocked(prisma.inboundDocument.findMany).mock
.calls) {
const call = args as {
where?: { deletedAt?: unknown };
select?: Record<string, unknown>;
};
if (call.where?.deletedAt === null) continue;
expect(Object.keys(call.select ?? {}).sort()).toEqual([
"deletedAt",
"sourceAliases",
"sourceId",
"sourceInstance",
"sourceSystem",
"userId",
]);
}
});
it("/api/export (legacy) scopes the measurement read to deletedAt: null", async () => {
@@ -67,6 +67,7 @@ vi.mock("@/lib/db", () => ({
// Empty for the same reason as the sections above.
documentConditionLink: { findMany: vi.fn().mockResolvedValue([]) },
extractedFact: { findMany: vi.fn().mockResolvedValue([]) },
documentImportKey: { findMany: vi.fn(async () => []) },
ecgRecording: { findMany: vi.fn().mockResolvedValue([]) },
symptomDefinition: { findMany: vi.fn().mockResolvedValue([]) },
lifeEvent: { findMany: vi.fn().mockResolvedValue([]) },
+2 -2
View File
@@ -39,7 +39,7 @@ interface ImportSourceKey {
}
/** The columns a row needs for {@link importKeysOf}. */
const SOURCE_KEY_SELECT = {
export const SOURCE_KEY_SELECT = {
userId: true,
sourceSystem: true,
sourceId: true,
@@ -62,7 +62,7 @@ interface KeyedRow {
}
/** Every import source key a document holds: its own, then its aliases. */
function importKeysOf(row: KeyedRow): ImportSourceKey[] {
export function importKeysOf(row: KeyedRow): ImportSourceKey[] {
return [
...(row.sourceSystem && row.sourceId
? [
@@ -463,6 +463,8 @@ function makePrisma() {
// Empty for the same reason as the sections above.
documentConditionLink: { findMany: vi.fn().mockResolvedValue([]) },
extractedFact: { findMany: vi.fn().mockResolvedValue([]) },
documentImportKey: { findMany: vi.fn().mockResolvedValue([]) },
inboundDocument: { findMany: vi.fn().mockResolvedValue([]) },
// The ECG strips. Empty for the same reason as the sections above:
// an account whose watch has never taken one is the ordinary case the
// builder must still answer for.
@@ -322,6 +322,7 @@ function sourceClient() {
// Empty for the same reason as the sections above.
documentConditionLink: { findMany: vi.fn().mockResolvedValue([]) },
extractedFact: { findMany: vi.fn().mockResolvedValue([]) },
documentImportKey: { findMany: vi.fn().mockResolvedValue([]) },
ecgRecording: { findMany: vi.fn().mockResolvedValue([]) },
symptomDefinition: { findMany: vi.fn().mockResolvedValue([]) },
lifeEvent: { findMany: vi.fn().mockResolvedValue([]) },
+3 -3
View File
@@ -159,6 +159,7 @@ export const BACKED_UP_MODELS = [
"InboundDocument",
"DocumentConditionLink",
"ExtractedFact",
"DocumentImportKey", // Deleted imports' keys: `document-filing-backup.ts`.
// ── Visits ────────────────────────────────────────────────────────────────
"Practitioner",
@@ -502,6 +503,7 @@ export const TWO_ENDED_MODELS = [
// nulled together with its type when it resolves to nothing.
"DocumentConditionLink",
"ExtractedFact",
"DocumentImportKey",
// What the account earned. The register called the bests "recomputable in
// principle", which is true and does not help: nothing recomputes them, and
// a recomputation over a restored history would not find the same rows:
@@ -768,12 +770,10 @@ export const NOT_IN_BACKUP_MODELS: Readonly<Record<string, string>> = {
"That this host's one-time fold repair has recomputed the account's hourly and daily means, which lets the compaction-tombstone purge delete the tombstones the repair reads. It describes this database, not the file: a restore of a file exported before the repair ran writes back the means and tombstones of that moment, so the restore removes the row and the repair runs again before anything is purged.",
BackupPassAttempt:
"When this host's backup passes last started and finished this account, kept so a pass the process died under is taken last next time and named on the backups page. It describes this deployment's own runs, so restoring it elsewhere would report an interrupted run that host never had, or hide one it did.",
DocumentImportKey:
"The source keys of imported documents the person deleted and the purge then removed, kept so a re-run of the importer does not store them again. The backup carries only live documents, so the tombstones that hold the same memory before the purge do not travel either; carrying the ledger alone would restore half of one decision. After a restore, a deliberate re-import can bring such a document back, and the person deletes it again.",
DocumentAiRun:
"A ticket for one background read (a document read with AI, a lab scan), deleted an hour after it finishes. The document and the committed rows travel; the ticket does not, and a restored run would point at a worker job that does not exist on the receiving host.",
DocumentSourceAlias:
"Further import source keys that were answered with an already stored document because the bytes matched. The next import run writes the same alias again the moment it meets the same bytes, so a restore loses nothing a re-run does not rebuild; and for a deleted document it restores no less than the tombstones and ledger beside it do, which do not travel either.",
"Further import source keys that were answered with an already stored document because the bytes matched. The next import run writes the same alias again the moment it meets the same bytes, so a restore loses nothing a re-run does not rebuild. A deleted document's aliases travel as import-ledger entries (`DocumentImportKey`), which is what they mean once the document is gone.",
ImportJob:
"A job record pointing at an uploaded file that the backup does not carry, so restoring it would resurrect a task with nothing to work on.",
HealthKitTypeSync:
+1
View File
@@ -167,6 +167,7 @@ const SUMMARY_COUNTS = [
"documents",
"documentConditionLinks",
"extractedFacts",
"documentImportKeys",
"healthProfile",
"healthProfileFactRevisions",
"customMetrics",
@@ -12,6 +12,11 @@ import {
ExtractedFactType,
} from "@/generated/prisma/enums";
import { base64BytesSchema, isoDateTime } from "@/lib/export/backup-schema-kit";
import {
DOCUMENT_SOURCE_ID_MAX,
DOCUMENT_SOURCE_INSTANCE_MAX,
DOCUMENT_SOURCE_SYSTEMS,
} from "@/lib/validations/inbound-documents";
/** One document filed against one condition episode. */
export const documentConditionLinkBackupSchema = z
@@ -54,3 +59,21 @@ export const extractedFactBackupSchema = z
updatedAt: isoDateTime,
})
.passthrough();
/**
* One import source key the person deleted the document of (the import
* ledger). Closed and bounded like the upload's own fields, so a restore
* cannot write a key the upload could never have stored.
*/
export const documentImportKeyBackupSchema = z
.object({
sourceSystem: z.enum(DOCUMENT_SOURCE_SYSTEMS),
sourceId: z.string().min(1).max(DOCUMENT_SOURCE_ID_MAX),
sourceInstance: z
.string()
.max(DOCUMENT_SOURCE_INSTANCE_MAX)
.nullable()
.optional(),
createdAt: isoDateTime.optional(),
})
.passthrough();
+143 -3
View File
@@ -14,6 +14,12 @@
* entries read "a restored vault is unsorted" and "re-derivable only by
* re-running the extraction against a provider, at the operator's cost".
*
* A third rides beside them: the import ledger (`DocumentImportKey`), the
* source keys of imported documents the person deleted, with the keys of the
* documents still waiting in Recently deleted folded in (those documents do
* not travel). Without it a restore onto a fresh instance let a token importer
* store every deliberately deleted document again.
*
* ## The state on a fact is worth more than the fact
*
* `status`, `needsReview` and the pair `committedRecordId` /
@@ -71,6 +77,7 @@
import type { Prisma, PrismaClient } from "@/generated/prisma/client";
import { decryptFromBytes, encryptToBytes } from "@/lib/ai/coach/bytes-codec";
import { importKeysOf, SOURCE_KEY_SELECT } from "@/lib/documents/tombstones";
import {
recordUnknownKeys,
type RestoreSkipLog,
@@ -112,14 +119,28 @@ export interface ExtractedFactBackupEntry {
updatedAt: string;
}
/**
* One import source key the person deleted the document of. The ledger's own
* rows (documents the purge removed) and the keys of the documents still
* waiting in Recently deleted, which the file does not carry as documents.
*/
export interface DocumentImportKeyBackupEntry {
sourceSystem: string;
sourceId: string;
sourceInstance: string | null;
createdAt: string;
}
export interface DocumentFilingBackupSection {
documentConditionLinks: DocumentConditionLinkBackupEntry[];
extractedFacts: ExtractedFactBackupEntry[];
documentImportKeys: DocumentImportKeyBackupEntry[];
}
export interface DocumentFilingBackupCounts {
documentConditionLinks: number;
extractedFacts: number;
documentImportKeys: number;
}
/**
@@ -165,8 +186,72 @@ function decryptJsonSoft(bytes: Uint8Array): unknown {
}
}
/**
* What the person deleted on purpose among their imported documents, as the
* import ledger would hold it once every deletion is purged: the ledger's own
* rows, then the keys (own and alias) of every document still waiting in
* Recently deleted. Those documents do not travel, as no tombstone ever has;
* without their keys a restore onto a fresh instance would let an importer
* store each one again. A key that appears twice is carried once.
*/
async function buildDeletedImportKeys(
prisma: Pick<PrismaClient, "documentImportKey" | "inboundDocument">,
userId: string,
): Promise<DocumentImportKeyBackupEntry[]> {
const [ledger, tombstones] = await Promise.all([
prisma.documentImportKey.findMany({
where: { userId },
orderBy: [{ createdAt: "asc" }, { id: "asc" }],
select: {
sourceSystem: true,
sourceId: true,
sourceInstance: true,
createdAt: true,
},
}),
prisma.inboundDocument.findMany({
where: {
userId,
deletedAt: { not: null },
OR: [
{ sourceSystem: { not: null }, sourceId: { not: null } },
{ sourceAliases: { some: {} } },
],
},
orderBy: [{ deletedAt: "asc" }, { id: "asc" }],
select: { ...SOURCE_KEY_SELECT, deletedAt: true },
}),
]);
const out: DocumentImportKeyBackupEntry[] = [];
const seen = new Set<string>();
const add = (
key: Omit<DocumentImportKeyBackupEntry, "createdAt">,
at: Date,
) => {
const id = JSON.stringify([
key.sourceSystem,
key.sourceInstance,
key.sourceId,
]);
if (seen.has(id)) return;
seen.add(id);
out.push({ ...key, createdAt: at.toISOString() });
};
for (const row of ledger) add(row, row.createdAt);
for (const row of tombstones) {
for (const key of importKeysOf(row)) add(key, row.deletedAt!);
}
return out;
}
export async function buildDocumentFilingBackupSection(
prisma: Pick<PrismaClient, "documentConditionLink" | "extractedFact">,
prisma: Pick<
PrismaClient,
| "documentConditionLink"
| "extractedFact"
| "documentImportKey"
| "inboundDocument"
>,
userId: string,
options: DocumentFilingBackupOptions = {},
): Promise<DocumentFilingBackupSection> {
@@ -177,7 +262,7 @@ export async function buildDocumentFilingBackupSection(
// skips tombstoned documents in BOTH purposes, and the episode reader skips
// tombstoned episodes in a portable export only — so the filters here mirror
// exactly what `records-backup.ts` decided to carry.
const [linkRows, factRows] = await Promise.all([
const [linkRows, factRows, documentImportKeys] = await Promise.all([
prisma.documentConditionLink.findMany({
where: {
userId,
@@ -192,6 +277,7 @@ export async function buildDocumentFilingBackupSection(
orderBy: [{ createdAt: "asc" }, { id: "asc" }],
select: EXTRACTED_FACT_BACKUP_SELECT,
}),
buildDeletedImportKeys(prisma, userId),
]);
return {
@@ -223,6 +309,7 @@ export async function buildDocumentFilingBackupSection(
createdAt: row.createdAt.toISOString(),
updatedAt: row.updatedAt.toISOString(),
})),
documentImportKeys,
};
}
@@ -233,6 +320,7 @@ export function countDocumentFilingBackupSection(
return {
documentConditionLinks: section.documentConditionLinks.length,
extractedFacts: section.extractedFacts.length,
documentImportKeys: section.documentImportKeys.length,
};
}
@@ -240,6 +328,7 @@ export function countDocumentFilingBackupSection(
export interface DocumentFilingRestoreCleared {
documentConditionLinks: number;
extractedFacts: number;
documentImportKeys: number;
}
type OptionalNullable<T> = { [K in keyof T]?: T[K] | undefined };
@@ -279,6 +368,18 @@ export type RestoredExtractedFact = Pick<
export interface DocumentFilingRestoreInput {
documentConditionLinks: DocumentConditionLinkBackupEntry[];
extractedFacts: RestoredExtractedFact[];
documentImportKeys: Array<
Pick<DocumentImportKeyBackupEntry, "sourceSystem" | "sourceId"> & {
sourceInstance?: string | null;
createdAt?: string;
}
>;
/** The restored documents' own keys: a live document's key is not deleted. */
documents: Array<{
sourceSystem?: string | null;
sourceId?: string | null;
sourceInstance?: string | null;
}>;
}
/**
@@ -319,10 +420,12 @@ export async function restoreDocumentFilingData(
refs: DocumentFilingRestoreRefs,
skips: RestoreSkipLog,
): Promise<DocumentFilingRestoreCleared> {
const [clearedLinks, clearedFacts] = await Promise.all([
const [clearedLinks, clearedFacts, clearedKeys] = await Promise.all([
tx.documentConditionLink.deleteMany({ where: { userId: ownerId } }),
tx.extractedFact.deleteMany({ where: { userId: ownerId } }),
tx.documentImportKey.deleteMany({ where: { userId: ownerId } }),
]);
await restoreDeletedImportKeys(tx, ownerId, payload);
const droppedFilings: string[] = [];
const writableLinks = payload.documentConditionLinks.filter((link) => {
@@ -416,9 +519,46 @@ export async function restoreDocumentFilingData(
return {
documentConditionLinks: clearedLinks.count,
extractedFacts: clearedFacts.count,
documentImportKeys: clearedKeys.count,
};
}
/**
* The import ledger back, as the file carries it, minus any key a restored
* live document holds: the ledger says "the person deleted this", and a
* document in the vault under that key says otherwise. A file written before
* the ledger travelled carries none, and the restore then remembers no
* deletion, as before.
*/
async function restoreDeletedImportKeys(
tx: Prisma.TransactionClient,
ownerId: string,
payload: DocumentFilingRestoreInput,
): Promise<void> {
const keyOf = (k: {
sourceSystem?: string | null;
sourceId?: string | null;
sourceInstance?: string | null;
}) => JSON.stringify([k.sourceSystem, k.sourceInstance ?? null, k.sourceId]);
const live = new Set(
payload.documents
.filter((d) => d.sourceSystem && d.sourceId)
.map((d) => keyOf(d)),
);
const keys = payload.documentImportKeys.filter((k) => !live.has(keyOf(k)));
if (keys.length === 0) return;
await tx.documentImportKey.createMany({
data: keys.map((k) => ({
userId: ownerId,
sourceSystem: k.sourceSystem,
sourceId: k.sourceId,
sourceInstance: k.sourceInstance ?? null,
...(k.createdAt ? { createdAt: new Date(k.createdAt) } : {}),
})),
skipDuplicates: true,
});
}
/**
* A fact column's stored bytes, whichever end of the contract the file came
* from.
+1
View File
@@ -72,6 +72,7 @@ export const RESTORE_CLEARED_KEYS = [
"documents",
"documentConditionLinks",
"extractedFacts",
"documentImportKeys",
"healthProfile",
"healthProfileFactRevisions",
"customMetrics",
+3
View File
@@ -59,6 +59,8 @@ export interface BackupSummary {
documentConditionLinks: number;
/** Facts staged out of a document, with their review decision. */
extractedFacts: number;
/** Import source keys of documents the person deleted. */
documentImportKeys: number;
/** 1 when the account's durable self-context rides the file, 0 otherwise. */
healthProfile: number;
/** Effective-dated structured health-profile revisions. */
@@ -159,6 +161,7 @@ export function summarizeBackup(payload: BackupPayload): BackupSummary {
// did for visits.
documentConditionLinks: payload.documentConditionLinks.length,
extractedFacts: payload.extractedFacts.length,
documentImportKeys: payload.documentImportKeys.length,
healthProfile: payload.healthProfile ? 1 : 0,
healthProfileFactRevisions: payload.healthProfileFacts.length,
customMetrics: payload.customMetrics.length,
+4
View File
@@ -51,6 +51,7 @@ import {
import {
documentConditionLinkBackupSchema,
extractedFactBackupSchema,
documentImportKeyBackupSchema,
} from "@/lib/export/document-filing-backup-schema";
import { documentBackupSchema } from "@/lib/export/documents-backup-schema";
import { ecgRecordingBackupSchema } from "@/lib/export/ecg-backup-schema";
@@ -182,6 +183,9 @@ export const backupPayloadSchema = z
.array(documentConditionLinkBackupSchema)
.default([]),
extractedFacts: z.array(extractedFactBackupSchema).default([]),
// The source keys of imported documents the person deleted. Defaulted:
// a file written before the import ledger travelled carries no key.
documentImportKeys: z.array(documentImportKeyBackupSchema).default([]),
nutrientDays: z.array(nutrientDaySchema).default([]),
// Durable self-context and user-defined series. Defaulted so files written
// before either rode the wire still parse; an account with neither writes
@@ -539,7 +539,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:00.000Z"
"createdAt": "2026-05-06T06:27:00.000Z"
},
{
"id": "<id:21>",
@@ -554,7 +554,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": 2,
"createdAt": "2026-05-06T04:47:02.000Z"
"createdAt": "2026-05-06T06:27:02.000Z"
},
{
"id": "<id:22>",
@@ -569,14 +569,14 @@
],
"source": "MANUAL",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:01.000Z"
"createdAt": "2026-05-06T06:27:01.000Z"
}
],
"efficacyTargets": [
{
"id": "<id:23>",
"createdAt": "2026-05-06T06:25:00.000Z",
"updatedAt": "2026-05-06T06:26:00.000Z",
"createdAt": "2026-05-06T08:05:00.000Z",
"updatedAt": "2026-05-06T08:06:00.000Z",
"measurementType": "WEIGHT",
"biomarkerName": "Ferritin",
"primary": false
@@ -972,7 +972,7 @@
"name": "Ferr.",
"source": "IMPORT_CHOICE",
"id": "<id:40>",
"createdAt": "2026-05-06T11:26:00.000Z"
"createdAt": "2026-05-06T13:06:00.000Z"
}
]
}
@@ -985,7 +985,7 @@
"sourceBiomarkerId": "merged-away-marker",
"sourceName": "Ferritin (Serum)",
"readingCount": 3,
"mergedAt": "2026-05-06T13:06:00.000Z"
"mergedAt": "2026-05-06T14:46:00.000Z"
}
],
"illnessEpisodes": [
@@ -1194,8 +1194,8 @@
"sourceSystem": "PAPERLESS",
"sourceId": "4711",
"sourceInstance": "https://paperless.example.test",
"createdAt": "2026-05-04T01:24:00.000Z",
"updatedAt": "2026-05-04T01:25:00.000Z",
"createdAt": "2026-05-04T01:25:00.000Z",
"updatedAt": "2026-05-04T01:26:00.000Z",
"contentEncrypted": "<sealed:v1:bytes:cm91bmQtdHJpcCBkb2N1bWVudCBieXRlcw==>",
"contentSha256": "0a738470f0945033965858e9895ee721ce981a25f013514d587d0a7a1946be7a",
"contentCodec": "binary2",
@@ -1206,7 +1206,8 @@
"summaryState": "PENDING",
"lastIndexAttemptAt": "2026-06-30T12:34:56.789Z",
"lastIndexOutcome": "rt-InboundDocument-lastIndexOutcome",
"aiReadDeferred": true
"aiReadDeferred": true,
"extractionIncomplete": true
}
],
"manifest": {
@@ -1456,8 +1457,8 @@
],
"onboardingRecord": {
"id": "<id:65>",
"createdAt": "2026-05-06T09:47:00.000Z",
"updatedAt": "2026-05-06T09:48:00.000Z",
"createdAt": "2026-05-06T11:27:00.000Z",
"updatedAt": "2026-05-06T11:28:00.000Z",
"needs": {
"recordTarget": "me",
"areas": [
@@ -1990,6 +1991,14 @@
"updatedAt": "2026-05-05T18:52:00.000Z"
}
],
"documentImportKeys": [
{
"sourceSystem": "PAPRA",
"sourceId": "doc_deleted",
"sourceInstance": "https://papra.example",
"createdAt": "2026-07-03T09:00:00.000Z"
}
],
"personalRecords": [
{
"metricType": "WALKING_RUNNING_DISTANCE",
@@ -2001,7 +2010,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-5km",
"createdAt": "2026-05-05T20:31:01.000Z"
"createdAt": "2026-05-05T22:11:01.000Z"
},
{
"metricType": "WALKING_RUNNING_DISTANCE",
@@ -2013,7 +2022,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-10km",
"createdAt": "2026-05-05T20:31:02.000Z"
"createdAt": "2026-05-05T22:11:02.000Z"
},
{
"metricType": "WEIGHT",
@@ -2025,19 +2034,19 @@
"sourceMeasurementId": "<id:4>",
"source": "MANUAL",
"externalId": null,
"createdAt": "2026-05-05T20:31:00.000Z"
"createdAt": "2026-05-05T22:11:00.000Z"
}
],
"userAchievements": [
{
"achievementId": "retired-badge-from-an-older-release",
"unlockedAt": "2025-11-05T08:00:00.000Z",
"createdAt": "2026-05-05T22:04:01.000Z"
"createdAt": "2026-05-05T23:44:01.000Z"
},
{
"achievementId": "intake-total-10",
"unlockedAt": "2026-02-21T10:15:00.000Z",
"createdAt": "2026-05-05T22:04:00.000Z"
"createdAt": "2026-05-05T23:44:00.000Z"
}
],
"environmentContexts": [
@@ -2083,8 +2092,8 @@
"aqHours": 3,
"aqFetchedAt": "2026-06-30T12:34:56.789Z",
"fetchedAt": "2026-06-16T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:00.000Z",
"updatedAt": "2026-05-06T00:21:00.000Z"
"createdAt": "2026-05-06T02:00:00.000Z",
"updatedAt": "2026-05-06T02:01:00.000Z"
},
{
"date": "2026-07-01",
@@ -2128,8 +2137,8 @@
"aqHours": null,
"aqFetchedAt": null,
"fetchedAt": "2026-07-02T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:01.000Z",
"updatedAt": "2026-05-06T00:21:01.000Z"
"createdAt": "2026-05-06T02:00:01.000Z",
"updatedAt": "2026-05-06T02:01:01.000Z"
}
],
"environmentTravelLocations": [
@@ -2140,8 +2149,8 @@
"lon": null,
"label": null,
"locationEncrypted": "<sealed:v1:{\"lat\":41.4,\"lon\":2.2,\"label\":\"Barcelona\"}>",
"createdAt": "2026-05-06T01:25:00.000Z",
"updatedAt": "2026-05-06T01:26:00.000Z"
"createdAt": "2026-05-06T03:05:00.000Z",
"updatedAt": "2026-05-06T03:06:00.000Z"
}
],
"dawarichDays": [
@@ -2156,8 +2165,8 @@
"countryCodeEncrypted": "<sealed:v1:country:ES>",
"isTravelDay": true,
"fetchedAt": "2026-06-16T03:20:00.000Z",
"createdAt": "2026-05-06T03:11:00.000Z",
"updatedAt": "2026-05-06T03:12:00.000Z"
"createdAt": "2026-05-06T04:51:00.000Z",
"updatedAt": "2026-05-06T04:52:00.000Z"
}
],
"ecgRecordings": [
@@ -2174,8 +2183,8 @@
"averageHeartRate": 88,
"rhythmClassification": "IRREGULAR",
"measurementId": "<id:5>",
"createdAt": "2026-05-06T08:13:00.000Z",
"updatedAt": "2026-05-06T08:14:00.000Z"
"createdAt": "2026-05-06T09:53:00.000Z",
"updatedAt": "2026-05-06T09:54:00.000Z"
}
],
"symptomDefinitions": [
@@ -2271,35 +2280,35 @@
{
"labResultId": "<id:37>",
"documentId": "<id:53>",
"createdAt": "2026-05-06T14:44:00.000Z"
"createdAt": "2026-05-06T16:24:00.000Z"
}
],
"encounterMedicationLinks": [
{
"encounterId": "<id:68>",
"medicationId": "<id:6>",
"createdAt": "2026-05-06T16:24:00.000Z"
"createdAt": "2026-05-06T18:04:00.000Z"
}
],
"encounterVaccinationLinks": [
{
"encounterId": "<id:68>",
"vaccinationId": "<id:73>",
"createdAt": "2026-05-06T18:04:00.000Z"
"createdAt": "2026-05-06T19:44:00.000Z"
}
],
"medicationDocumentLinks": [
{
"medicationId": "<id:6>",
"documentId": "<id:53>",
"createdAt": "2026-05-06T19:44:00.000Z"
"createdAt": "2026-05-06T21:24:00.000Z"
}
],
"medicationConditionLinks": [
{
"medicationId": "<id:6>",
"episodeId": "<id:44>",
"createdAt": "2026-05-06T21:24:00.000Z"
"createdAt": "2026-05-06T23:04:00.000Z"
}
],
"nutrientDays": [
@@ -2367,6 +2376,7 @@
"consentReceipts": 2,
"documentConditionLinks": 1,
"extractedFacts": 1,
"documentImportKeys": 1,
"personalRecords": 3,
"userAchievements": 2,
"environmentContexts": 2,
@@ -2430,6 +2440,7 @@
"consentReceipts": 2,
"documentConditionLinks": 1,
"extractedFacts": 1,
"documentImportKeys": 1,
"personalRecords": 3,
"userAchievements": 2,
"environmentContexts": 2,
@@ -2942,8 +2953,8 @@
"measurementType": "WEIGHT",
"biomarkerName": "Ferritin",
"primary": false,
"createdAt": "2026-05-06T06:25:00.000Z",
"updatedAt": "2026-05-06T06:26:00.000Z"
"createdAt": "2026-05-06T08:05:00.000Z",
"updatedAt": "2026-05-06T08:06:00.000Z"
}
],
"scheduleRevisions": [
@@ -2960,7 +2971,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:00.000Z"
"createdAt": "2026-05-06T06:27:00.000Z"
},
{
"id": "<id:21>",
@@ -2975,7 +2986,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": 2,
"createdAt": "2026-05-06T04:47:02.000Z"
"createdAt": "2026-05-06T06:27:02.000Z"
},
{
"id": "<id:22>",
@@ -2990,7 +3001,7 @@
],
"source": "MANUAL",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:01.000Z"
"createdAt": "2026-05-06T06:27:01.000Z"
}
],
"courses": [
@@ -3412,7 +3423,7 @@
"id": "<id:40>",
"name": "Ferr.",
"source": "IMPORT_CHOICE",
"createdAt": "2026-05-06T11:26:00.000Z"
"createdAt": "2026-05-06T13:06:00.000Z"
}
],
"createdAt": "2026-05-02T12:30:00.000Z",
@@ -3427,7 +3438,7 @@
"sourceBiomarkerId": "merged-away-marker",
"sourceName": "Ferritin (Serum)",
"readingCount": 3,
"mergedAt": "2026-05-06T13:06:00.000Z"
"mergedAt": "2026-05-06T14:46:00.000Z"
}
],
"illnessEpisodes": [
@@ -3736,8 +3747,9 @@
"sourceId": "4711",
"sourceInstance": "https://paperless.example.test",
"aiReadDeferred": true,
"createdAt": "2026-05-04T01:24:00.000Z",
"updatedAt": "2026-05-04T01:25:00.000Z"
"extractionIncomplete": true,
"createdAt": "2026-05-04T01:25:00.000Z",
"updatedAt": "2026-05-04T01:26:00.000Z"
}
],
"documentConditionLinks": [
@@ -3763,6 +3775,14 @@
"updatedAt": "2026-05-05T18:52:00.000Z"
}
],
"documentImportKeys": [
{
"sourceSystem": "PAPRA",
"sourceId": "doc_deleted",
"sourceInstance": "https://papra.example",
"createdAt": "2026-07-03T09:00:00.000Z"
}
],
"nutrientDays": [
{
"day": "2026-07-01",
@@ -4064,8 +4084,8 @@
},
"modulesDerivedAt": "2026-07-01T08:00:00.000Z",
"completedAt": "2026-07-01T08:00:00.000Z",
"createdAt": "2026-05-06T09:47:00.000Z",
"updatedAt": "2026-05-06T09:48:00.000Z"
"createdAt": "2026-05-06T11:27:00.000Z",
"updatedAt": "2026-05-06T11:28:00.000Z"
},
"practitioners": [
{
@@ -4226,35 +4246,35 @@
{
"labResultId": "<id:37>",
"documentId": "<id:53>",
"createdAt": "2026-05-06T14:44:00.000Z"
"createdAt": "2026-05-06T16:24:00.000Z"
}
],
"encounterMedicationLinks": [
{
"encounterId": "<id:68>",
"medicationId": "<id:6>",
"createdAt": "2026-05-06T16:24:00.000Z"
"createdAt": "2026-05-06T18:04:00.000Z"
}
],
"encounterVaccinationLinks": [
{
"encounterId": "<id:68>",
"vaccinationId": "<id:73>",
"createdAt": "2026-05-06T18:04:00.000Z"
"createdAt": "2026-05-06T19:44:00.000Z"
}
],
"medicationDocumentLinks": [
{
"medicationId": "<id:6>",
"documentId": "<id:53>",
"createdAt": "2026-05-06T19:44:00.000Z"
"createdAt": "2026-05-06T21:24:00.000Z"
}
],
"medicationConditionLinks": [
{
"medicationId": "<id:6>",
"episodeId": "<id:44>",
"createdAt": "2026-05-06T21:24:00.000Z"
"createdAt": "2026-05-06T23:04:00.000Z"
}
],
"coachConversations": [
@@ -4560,7 +4580,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-5km",
"createdAt": "2026-05-05T20:31:01.000Z"
"createdAt": "2026-05-05T22:11:01.000Z"
},
{
"metricType": "WALKING_RUNNING_DISTANCE",
@@ -4572,7 +4592,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-10km",
"createdAt": "2026-05-05T20:31:02.000Z"
"createdAt": "2026-05-05T22:11:02.000Z"
},
{
"metricType": "WEIGHT",
@@ -4584,19 +4604,19 @@
"sourceMeasurementId": "<id:4>",
"source": "MANUAL",
"externalId": null,
"createdAt": "2026-05-05T20:31:00.000Z"
"createdAt": "2026-05-05T22:11:00.000Z"
}
],
"userAchievements": [
{
"achievementId": "retired-badge-from-an-older-release",
"unlockedAt": "2025-11-05T08:00:00.000Z",
"createdAt": "2026-05-05T22:04:01.000Z"
"createdAt": "2026-05-05T23:44:01.000Z"
},
{
"achievementId": "intake-total-10",
"unlockedAt": "2026-02-21T10:15:00.000Z",
"createdAt": "2026-05-05T22:04:00.000Z"
"createdAt": "2026-05-05T23:44:00.000Z"
}
],
"environmentContexts": [
@@ -4642,8 +4662,8 @@
"aqHours": 3,
"aqFetchedAt": "2026-06-30T12:34:56.789Z",
"fetchedAt": "2026-06-16T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:00.000Z",
"updatedAt": "2026-05-06T00:21:00.000Z"
"createdAt": "2026-05-06T02:00:00.000Z",
"updatedAt": "2026-05-06T02:01:00.000Z"
},
{
"date": "2026-07-01",
@@ -4687,8 +4707,8 @@
"aqHours": null,
"aqFetchedAt": null,
"fetchedAt": "2026-07-02T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:01.000Z",
"updatedAt": "2026-05-06T00:21:01.000Z"
"createdAt": "2026-05-06T02:00:01.000Z",
"updatedAt": "2026-05-06T02:01:01.000Z"
}
],
"environmentTravelLocations": [
@@ -4699,8 +4719,8 @@
"lon": null,
"label": null,
"locationEncrypted": "<sealed:v1:{\"lat\":41.4,\"lon\":2.2,\"label\":\"Barcelona\"}>",
"createdAt": "2026-05-06T01:25:00.000Z",
"updatedAt": "2026-05-06T01:26:00.000Z"
"createdAt": "2026-05-06T03:05:00.000Z",
"updatedAt": "2026-05-06T03:06:00.000Z"
}
],
"dawarichDays": [
@@ -4715,8 +4735,8 @@
"countryCodeEncrypted": "<sealed:v1:country:ES>",
"isTravelDay": true,
"fetchedAt": "2026-06-16T03:20:00.000Z",
"createdAt": "2026-05-06T03:11:00.000Z",
"updatedAt": "2026-05-06T03:12:00.000Z"
"createdAt": "2026-05-06T04:51:00.000Z",
"updatedAt": "2026-05-06T04:52:00.000Z"
}
],
"ecgRecordings": [
@@ -4733,8 +4753,8 @@
"averageHeartRate": 88,
"rhythmClassification": "IRREGULAR",
"measurementId": "<id:5>",
"createdAt": "2026-05-06T08:13:00.000Z",
"updatedAt": "2026-05-06T08:14:00.000Z"
"createdAt": "2026-05-06T09:53:00.000Z",
"updatedAt": "2026-05-06T09:54:00.000Z"
}
],
"manifest": {
@@ -4781,6 +4801,7 @@
"documents": 1,
"documentConditionLinks": 1,
"extractedFacts": 1,
"documentImportKeys": 1,
"healthProfile": 1,
"healthProfileFactRevisions": 2,
"customMetrics": 2,
@@ -4837,6 +4858,7 @@
"documents": 1,
"documentConditionLinks": 1,
"extractedFacts": 1,
"documentImportKeys": 1,
"healthProfile": 1,
"healthProfileFactRevisions": 2,
"customMetrics": 2,
@@ -345,7 +345,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:00.000Z"
"createdAt": "2026-05-06T06:27:00.000Z"
},
{
"validFrom": "2026-02-01T00:00:00.000Z",
@@ -359,7 +359,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": 2,
"createdAt": "2026-05-06T04:47:02.000Z"
"createdAt": "2026-05-06T06:27:02.000Z"
},
{
"validFrom": "2026-03-01T00:00:00.000Z",
@@ -373,7 +373,7 @@
],
"source": "MANUAL",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:01.000Z"
"createdAt": "2026-05-06T06:27:01.000Z"
}
],
"efficacyTargets": [
@@ -576,7 +576,7 @@
"sourceBiomarkerId": "merged-away-marker",
"sourceName": "Ferritin (Serum)",
"readingCount": 3,
"mergedAt": "2026-05-06T13:06:00.000Z"
"mergedAt": "2026-05-06T14:46:00.000Z"
}
],
"illnessEpisodes": [
@@ -700,7 +700,7 @@
"sourceSystem": "PAPERLESS",
"sourceId": "4711",
"sourceInstance": "https://paperless.example.test",
"createdAt": "2026-05-04T01:24:00.000Z",
"createdAt": "2026-05-04T01:25:00.000Z",
"summary": "rt-InboundDocument-summaryEncrypted"
}
],
@@ -1228,6 +1228,14 @@
"updatedAt": "2026-05-05T18:52:00.000Z"
}
],
"documentImportKeys": [
{
"sourceSystem": "PAPRA",
"sourceId": "doc_deleted",
"sourceInstance": "https://papra.example",
"createdAt": "2026-07-03T09:00:00.000Z"
}
],
"personalRecords": [
{
"metricType": "WALKING_RUNNING_DISTANCE",
@@ -1239,7 +1247,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-5km",
"createdAt": "2026-05-05T20:31:01.000Z"
"createdAt": "2026-05-05T22:11:01.000Z"
},
{
"metricType": "WALKING_RUNNING_DISTANCE",
@@ -1251,7 +1259,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-10km",
"createdAt": "2026-05-05T20:31:02.000Z"
"createdAt": "2026-05-05T22:11:02.000Z"
},
{
"metricType": "WEIGHT",
@@ -1263,19 +1271,19 @@
"sourceMeasurementId": "<id:3>",
"source": "MANUAL",
"externalId": null,
"createdAt": "2026-05-05T20:31:00.000Z"
"createdAt": "2026-05-05T22:11:00.000Z"
}
],
"userAchievements": [
{
"achievementId": "retired-badge-from-an-older-release",
"unlockedAt": "2025-11-05T08:00:00.000Z",
"createdAt": "2026-05-05T22:04:01.000Z"
"createdAt": "2026-05-05T23:44:01.000Z"
},
{
"achievementId": "intake-total-10",
"unlockedAt": "2026-02-21T10:15:00.000Z",
"createdAt": "2026-05-05T22:04:00.000Z"
"createdAt": "2026-05-05T23:44:00.000Z"
}
],
"environmentContexts": [
@@ -1320,8 +1328,8 @@
"aqHours": 3,
"aqFetchedAt": "2026-06-30T12:34:56.789Z",
"fetchedAt": "2026-06-16T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:00.000Z",
"updatedAt": "2026-05-06T00:21:00.000Z"
"createdAt": "2026-05-06T02:00:00.000Z",
"updatedAt": "2026-05-06T02:01:00.000Z"
},
{
"date": "2026-07-01",
@@ -1364,8 +1372,8 @@
"aqHours": null,
"aqFetchedAt": null,
"fetchedAt": "2026-07-02T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:01.000Z",
"updatedAt": "2026-05-06T00:21:01.000Z"
"createdAt": "2026-05-06T02:00:01.000Z",
"updatedAt": "2026-05-06T02:01:01.000Z"
}
],
"environmentTravelLocations": [
@@ -1375,8 +1383,8 @@
"lat": 41.4,
"lon": 2.2,
"label": "Barcelona",
"createdAt": "2026-05-06T01:25:00.000Z",
"updatedAt": "2026-05-06T01:26:00.000Z"
"createdAt": "2026-05-06T03:05:00.000Z",
"updatedAt": "2026-05-06T03:06:00.000Z"
}
],
"dawarichDays": [
@@ -1390,8 +1398,8 @@
"countryCode": "ES",
"isTravelDay": true,
"fetchedAt": "2026-06-16T03:20:00.000Z",
"createdAt": "2026-05-06T03:11:00.000Z",
"updatedAt": "2026-05-06T03:12:00.000Z"
"createdAt": "2026-05-06T04:51:00.000Z",
"updatedAt": "2026-05-06T04:52:00.000Z"
}
],
"ecgRecordings": [
@@ -1416,8 +1424,8 @@
"averageHeartRate": 88,
"rhythmClassification": "IRREGULAR",
"measurementId": "<id:4>",
"createdAt": "2026-05-06T08:13:00.000Z",
"updatedAt": "2026-05-06T08:14:00.000Z"
"createdAt": "2026-05-06T09:53:00.000Z",
"updatedAt": "2026-05-06T09:54:00.000Z"
}
],
"symptomDefinitions": [
@@ -1492,35 +1500,35 @@
{
"labResultId": "<id:8>",
"documentId": "<id:15>",
"createdAt": "2026-05-06T14:44:00.000Z"
"createdAt": "2026-05-06T16:24:00.000Z"
}
],
"encounterMedicationLinks": [
{
"encounterId": "<id:17>",
"medicationId": "<id:5>",
"createdAt": "2026-05-06T16:24:00.000Z"
"createdAt": "2026-05-06T18:04:00.000Z"
}
],
"encounterVaccinationLinks": [
{
"encounterId": "<id:17>",
"vaccinationId": "<id:20>",
"createdAt": "2026-05-06T18:04:00.000Z"
"createdAt": "2026-05-06T19:44:00.000Z"
}
],
"medicationDocumentLinks": [
{
"medicationId": "<id:5>",
"documentId": "<id:15>",
"createdAt": "2026-05-06T19:44:00.000Z"
"createdAt": "2026-05-06T21:24:00.000Z"
}
],
"medicationConditionLinks": [
{
"medicationId": "<id:5>",
"episodeId": "<id:11>",
"createdAt": "2026-05-06T21:24:00.000Z"
"createdAt": "2026-05-06T23:04:00.000Z"
}
],
"nutrientDays": [
@@ -1587,6 +1595,7 @@
"consentReceipts": 0,
"documentConditionLinks": 1,
"extractedFacts": 1,
"documentImportKeys": 1,
"personalRecords": 3,
"userAchievements": 2,
"environmentContexts": 2,
@@ -1650,6 +1659,7 @@
"consentReceipts": 0,
"documentConditionLinks": 1,
"extractedFacts": 1,
"documentImportKeys": 1,
"personalRecords": 3,
"userAchievements": 2,
"environmentContexts": 2,
@@ -1989,7 +1999,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:00.000Z"
"createdAt": "2026-05-06T06:27:00.000Z"
},
{
"validFrom": "2026-02-01T00:00:00.000Z",
@@ -2003,7 +2013,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": 2,
"createdAt": "2026-05-06T04:47:02.000Z"
"createdAt": "2026-05-06T06:27:02.000Z"
},
{
"validFrom": "2026-03-01T00:00:00.000Z",
@@ -2017,7 +2027,7 @@
],
"source": "MANUAL",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:01.000Z"
"createdAt": "2026-05-06T06:27:01.000Z"
}
],
"courses": [
@@ -2238,7 +2248,7 @@
"sourceBiomarkerId": "merged-away-marker",
"sourceName": "Ferritin (Serum)",
"readingCount": 3,
"mergedAt": "2026-05-06T13:06:00.000Z"
"mergedAt": "2026-05-06T14:46:00.000Z"
}
],
"illnessEpisodes": [
@@ -2431,7 +2441,7 @@
"sourceId": "4711",
"sourceInstance": "https://paperless.example.test",
"summary": "rt-InboundDocument-summaryEncrypted",
"createdAt": "2026-05-04T01:24:00.000Z"
"createdAt": "2026-05-04T01:25:00.000Z"
}
],
"documentConditionLinks": [
@@ -2473,6 +2483,14 @@
"updatedAt": "2026-05-05T18:52:00.000Z"
}
],
"documentImportKeys": [
{
"sourceSystem": "PAPRA",
"sourceId": "doc_deleted",
"sourceInstance": "https://papra.example",
"createdAt": "2026-07-03T09:00:00.000Z"
}
],
"nutrientDays": [
{
"day": "2026-07-01",
@@ -2790,35 +2808,35 @@
{
"labResultId": "<id:8>",
"documentId": "<id:15>",
"createdAt": "2026-05-06T14:44:00.000Z"
"createdAt": "2026-05-06T16:24:00.000Z"
}
],
"encounterMedicationLinks": [
{
"encounterId": "<id:17>",
"medicationId": "<id:5>",
"createdAt": "2026-05-06T16:24:00.000Z"
"createdAt": "2026-05-06T18:04:00.000Z"
}
],
"encounterVaccinationLinks": [
{
"encounterId": "<id:17>",
"vaccinationId": "<id:20>",
"createdAt": "2026-05-06T18:04:00.000Z"
"createdAt": "2026-05-06T19:44:00.000Z"
}
],
"medicationDocumentLinks": [
{
"medicationId": "<id:5>",
"documentId": "<id:15>",
"createdAt": "2026-05-06T19:44:00.000Z"
"createdAt": "2026-05-06T21:24:00.000Z"
}
],
"medicationConditionLinks": [
{
"medicationId": "<id:5>",
"episodeId": "<id:11>",
"createdAt": "2026-05-06T21:24:00.000Z"
"createdAt": "2026-05-06T23:04:00.000Z"
}
],
"coachConversations": [
@@ -2996,7 +3014,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-5km",
"createdAt": "2026-05-05T20:31:01.000Z"
"createdAt": "2026-05-05T22:11:01.000Z"
},
{
"metricType": "WALKING_RUNNING_DISTANCE",
@@ -3008,7 +3026,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-10km",
"createdAt": "2026-05-05T20:31:02.000Z"
"createdAt": "2026-05-05T22:11:02.000Z"
},
{
"metricType": "WEIGHT",
@@ -3020,19 +3038,19 @@
"sourceMeasurementId": "<id:3>",
"source": "MANUAL",
"externalId": null,
"createdAt": "2026-05-05T20:31:00.000Z"
"createdAt": "2026-05-05T22:11:00.000Z"
}
],
"userAchievements": [
{
"achievementId": "retired-badge-from-an-older-release",
"unlockedAt": "2025-11-05T08:00:00.000Z",
"createdAt": "2026-05-05T22:04:01.000Z"
"createdAt": "2026-05-05T23:44:01.000Z"
},
{
"achievementId": "intake-total-10",
"unlockedAt": "2026-02-21T10:15:00.000Z",
"createdAt": "2026-05-05T22:04:00.000Z"
"createdAt": "2026-05-05T23:44:00.000Z"
}
],
"environmentContexts": [
@@ -3077,8 +3095,8 @@
"aqHours": 3,
"aqFetchedAt": "2026-06-30T12:34:56.789Z",
"fetchedAt": "2026-06-16T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:00.000Z",
"updatedAt": "2026-05-06T00:21:00.000Z"
"createdAt": "2026-05-06T02:00:00.000Z",
"updatedAt": "2026-05-06T02:01:00.000Z"
},
{
"date": "2026-07-01",
@@ -3121,8 +3139,8 @@
"aqHours": null,
"aqFetchedAt": null,
"fetchedAt": "2026-07-02T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:01.000Z",
"updatedAt": "2026-05-06T00:21:01.000Z"
"createdAt": "2026-05-06T02:00:01.000Z",
"updatedAt": "2026-05-06T02:01:01.000Z"
}
],
"environmentTravelLocations": [
@@ -3132,8 +3150,8 @@
"lat": 41.4,
"lon": 2.2,
"label": "Barcelona",
"createdAt": "2026-05-06T01:25:00.000Z",
"updatedAt": "2026-05-06T01:26:00.000Z"
"createdAt": "2026-05-06T03:05:00.000Z",
"updatedAt": "2026-05-06T03:06:00.000Z"
}
],
"dawarichDays": [
@@ -3147,8 +3165,8 @@
"countryCode": "ES",
"isTravelDay": true,
"fetchedAt": "2026-06-16T03:20:00.000Z",
"createdAt": "2026-05-06T03:11:00.000Z",
"updatedAt": "2026-05-06T03:12:00.000Z"
"createdAt": "2026-05-06T04:51:00.000Z",
"updatedAt": "2026-05-06T04:52:00.000Z"
}
],
"ecgRecordings": [
@@ -3173,8 +3191,8 @@
"averageHeartRate": 88,
"rhythmClassification": "IRREGULAR",
"measurementId": "<id:4>",
"createdAt": "2026-05-06T08:13:00.000Z",
"updatedAt": "2026-05-06T08:14:00.000Z"
"createdAt": "2026-05-06T09:53:00.000Z",
"updatedAt": "2026-05-06T09:54:00.000Z"
}
],
"manifest": {
@@ -3221,6 +3239,7 @@
"documents": 1,
"documentConditionLinks": 1,
"extractedFacts": 1,
"documentImportKeys": 1,
"healthProfile": 1,
"healthProfileFactRevisions": 0,
"customMetrics": 1,
@@ -3277,6 +3296,7 @@
"documents": 1,
"documentConditionLinks": 1,
"extractedFacts": 1,
"documentImportKeys": 1,
"healthProfile": 1,
"healthProfileFactRevisions": 0,
"customMetrics": 1,
@@ -27,6 +27,7 @@
"documents": 1,
"documentConditionLinks": 1,
"extractedFacts": 1,
"documentImportKeys": 1,
"healthProfile": 1,
"healthProfileFactRevisions": 2,
"customMetrics": 2,
@@ -83,6 +84,7 @@
"documents": 1,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 1,
"healthProfile": 1,
"healthProfileFactRevisions": 2,
"customMetrics": 2,
@@ -299,6 +301,7 @@
"documents": 1,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 1,
"healthProfile": 1,
"healthProfileFactRevisions": 2,
"customMetrics": 2,
@@ -27,6 +27,7 @@
"documents": 1,
"documentConditionLinks": 1,
"extractedFacts": 1,
"documentImportKeys": 1,
"healthProfile": 1,
"healthProfileFactRevisions": 2,
"customMetrics": 2,
@@ -83,6 +84,7 @@
"documents": 0,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 0,
"healthProfile": 0,
"healthProfileFactRevisions": 0,
"customMetrics": 0,
@@ -299,6 +301,7 @@
"documents": 0,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 0,
"healthProfile": 0,
"healthProfileFactRevisions": 0,
"customMetrics": 0,
@@ -1641,8 +1644,9 @@
"sourceId": "4711",
"sourceInstance": "https://paperless.example.test",
"aiReadDeferred": true,
"createdAt": "2026-05-04T01:24:00.000Z",
"updatedAt": "2026-05-04T01:25:00.000Z",
"extractionIncomplete": true,
"createdAt": "2026-05-04T01:25:00.000Z",
"updatedAt": "2026-05-04T01:26:00.000Z",
"deletedAt": null
}
],
@@ -2245,10 +2249,20 @@
"updatedAt": "2026-05-05T18:52:00.000Z"
}
],
"PersonalRecord": [
"DocumentImportKey": [
{
"id": "<id:104>",
"userId": "round-trip-owner",
"sourceSystem": "PAPRA",
"sourceId": "doc_deleted",
"sourceInstance": "https://papra.example",
"createdAt": "2026-07-03T09:00:00.000Z"
}
],
"PersonalRecord": [
{
"id": "<id:105>",
"userId": "round-trip-owner",
"metricType": "WALKING_RUNNING_DISTANCE",
"metricSlot": "running_10km_time",
"direction": "MIN",
@@ -2258,10 +2272,10 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-10km",
"createdAt": "2026-05-05T20:31:02.000Z"
"createdAt": "2026-05-05T22:11:02.000Z"
},
{
"id": "<id:105>",
"id": "<id:106>",
"userId": "round-trip-owner",
"metricType": "WALKING_RUNNING_DISTANCE",
"metricSlot": "running_5km_time",
@@ -2272,10 +2286,10 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-5km",
"createdAt": "2026-05-05T20:31:01.000Z"
"createdAt": "2026-05-05T22:11:01.000Z"
},
{
"id": "<id:106>",
"id": "<id:107>",
"userId": "round-trip-owner",
"metricType": "WEIGHT",
"metricSlot": null,
@@ -2286,28 +2300,28 @@
"sourceMeasurementId": "<id:5>",
"source": "MANUAL",
"externalId": null,
"createdAt": "2026-05-05T20:31:00.000Z"
"createdAt": "2026-05-05T22:11:00.000Z"
}
],
"UserAchievement": [
{
"id": "<id:107>",
"id": "<id:108>",
"userId": "round-trip-owner",
"achievementId": "intake-total-10",
"unlockedAt": "2026-02-21T10:15:00.000Z",
"createdAt": "2026-05-05T22:04:00.000Z"
"createdAt": "2026-05-05T23:44:00.000Z"
},
{
"id": "<id:108>",
"id": "<id:109>",
"userId": "round-trip-owner",
"achievementId": "retired-badge-from-an-older-release",
"unlockedAt": "2025-11-05T08:00:00.000Z",
"createdAt": "2026-05-05T22:04:01.000Z"
"createdAt": "2026-05-05T23:44:01.000Z"
}
],
"EnvironmentContext": [
{
"id": "<id:109>",
"id": "<id:110>",
"userId": "round-trip-owner",
"date": "2026-06-15",
"locationEncrypted": "<sealed:v1:{\"lat\":41.4,\"lon\":2.2,\"label\":\"Barcelona\"}>",
@@ -2347,11 +2361,11 @@
"aqHours": 3,
"aqFetchedAt": "2026-06-30T12:34:56.789Z",
"fetchedAt": "2026-06-16T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:00.000Z",
"updatedAt": "2026-05-06T00:21:00.000Z"
"createdAt": "2026-05-06T02:00:00.000Z",
"updatedAt": "2026-05-06T02:01:00.000Z"
},
{
"id": "<id:110>",
"id": "<id:111>",
"userId": "round-trip-owner",
"date": "2026-07-01",
"locationEncrypted": "<sealed:v1:{\"lat\":52.5,\"lon\":13.4,\"label\":\"Berlin\"}>",
@@ -2391,24 +2405,24 @@
"aqHours": null,
"aqFetchedAt": null,
"fetchedAt": "2026-07-02T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:01.000Z",
"updatedAt": "2026-05-06T00:21:01.000Z"
"createdAt": "2026-05-06T02:00:01.000Z",
"updatedAt": "2026-05-06T02:01:01.000Z"
}
],
"EnvironmentTravelLocation": [
{
"id": "<id:111>",
"id": "<id:112>",
"userId": "round-trip-owner",
"startDate": "2026-06-10",
"endDate": "2026-06-20",
"locationEncrypted": "<sealed:v1:{\"lat\":41.4,\"lon\":2.2,\"label\":\"Barcelona\"}>",
"createdAt": "2026-05-06T01:25:00.000Z",
"updatedAt": "2026-05-06T01:26:00.000Z"
"createdAt": "2026-05-06T03:05:00.000Z",
"updatedAt": "2026-05-06T03:06:00.000Z"
}
],
"DawarichDay": [
{
"id": "<id:112>",
"id": "<id:113>",
"userId": "round-trip-owner",
"date": "2026-06-15",
"totalDistanceM": 48300,
@@ -2419,13 +2433,13 @@
"countryCodeEncrypted": "<sealed:v1:country:ES>",
"isTravelDay": true,
"fetchedAt": "2026-06-16T03:20:00.000Z",
"createdAt": "2026-05-06T03:11:00.000Z",
"updatedAt": "2026-05-06T03:12:00.000Z"
"createdAt": "2026-05-06T04:51:00.000Z",
"updatedAt": "2026-05-06T04:52:00.000Z"
}
],
"MedicationScheduleRevision": [
{
"id": "<id:113>",
"id": "<id:114>",
"medicationId": "<id:7>",
"validFrom": "2026-01-01T00:00:00.000Z",
"validUntil": "2026-03-01T00:00:00.000Z",
@@ -2438,10 +2452,10 @@
],
"source": "ARCHIVED",
"supersededByRevisionId": null,
"createdAt": "2026-05-06T04:47:00.000Z"
"createdAt": "2026-05-06T06:27:00.000Z"
},
{
"id": "<id:114>",
"id": "<id:115>",
"medicationId": "<id:7>",
"validFrom": "2026-02-01T00:00:00.000Z",
"validUntil": "2026-03-01T00:00:00.000Z",
@@ -2453,11 +2467,11 @@
}
],
"source": "ARCHIVED",
"supersededByRevisionId": "<id:115>",
"createdAt": "2026-05-06T04:47:02.000Z"
"supersededByRevisionId": "<id:116>",
"createdAt": "2026-05-06T06:27:02.000Z"
},
{
"id": "<id:115>",
"id": "<id:116>",
"medicationId": "<id:7>",
"validFrom": "2026-03-01T00:00:00.000Z",
"validUntil": "2026-05-01T00:00:00.000Z",
@@ -2470,23 +2484,23 @@
],
"source": "MANUAL",
"supersededByRevisionId": null,
"createdAt": "2026-05-06T04:47:01.000Z"
"createdAt": "2026-05-06T06:27:01.000Z"
}
],
"MedicationEfficacyTarget": [
{
"id": "<id:116>",
"id": "<id:117>",
"medicationId": "<id:7>",
"measurementType": "WEIGHT",
"biomarkerId": "<id:26>",
"primary": false,
"createdAt": "2026-05-06T06:25:00.000Z",
"updatedAt": "2026-05-06T06:26:00.000Z"
"createdAt": "2026-05-06T08:05:00.000Z",
"updatedAt": "2026-05-06T08:06:00.000Z"
}
],
"EcgRecording": [
{
"id": "<id:117>",
"id": "<id:118>",
"userId": "round-trip-owner",
"source": "MANUAL",
"externalRecordingId": "round-trip-signal-1",
@@ -2499,13 +2513,13 @@
"averageHeartRate": 88,
"rhythmClassification": "IRREGULAR",
"measurementId": "<id:2>",
"createdAt": "2026-05-06T08:13:00.000Z",
"updatedAt": "2026-05-06T08:14:00.000Z"
"createdAt": "2026-05-06T09:53:00.000Z",
"updatedAt": "2026-05-06T09:54:00.000Z"
}
],
"OnboardingRecord": [
{
"id": "<id:118>",
"id": "<id:119>",
"userId": "round-trip-owner",
"needsJson": {
"areas": [
@@ -2568,80 +2582,80 @@
},
"modulesDerivedAt": "2026-07-01T08:00:00.000Z",
"completedAt": "2026-07-01T08:00:00.000Z",
"createdAt": "2026-05-06T09:47:00.000Z",
"updatedAt": "2026-05-06T09:48:00.000Z"
"createdAt": "2026-05-06T11:27:00.000Z",
"updatedAt": "2026-05-06T11:28:00.000Z"
}
],
"BiomarkerAlias": [
{
"id": "<id:119>",
"id": "<id:120>",
"userId": "round-trip-owner",
"biomarkerId": "<id:26>",
"name": "Ferr.",
"nameFolded": "ferr.",
"source": "IMPORT_CHOICE",
"createdAt": "2026-05-06T11:26:00.000Z"
"createdAt": "2026-05-06T13:06:00.000Z"
}
],
"BiomarkerMerge": [
{
"id": "<id:120>",
"id": "<id:121>",
"userId": "round-trip-owner",
"targetBiomarkerId": "<id:26>",
"sourceBiomarkerId": "merged-away-marker",
"sourceName": "Ferritin (Serum)",
"readingCount": 3,
"mergedAt": "2026-05-06T13:06:00.000Z"
"mergedAt": "2026-05-06T14:46:00.000Z"
}
],
"LabResultDocumentLink": [
{
"id": "<id:121>",
"id": "<id:122>",
"labResultId": "<id:25>",
"documentId": "<id:65>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T14:44:00.000Z"
}
],
"EncounterMedicationLink": [
{
"id": "<id:122>",
"encounterId": "<id:69>",
"medicationId": "<id:7>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T16:24:00.000Z"
}
],
"EncounterVaccinationLink": [
"EncounterMedicationLink": [
{
"id": "<id:123>",
"encounterId": "<id:69>",
"vaccinationId": "<id:75>",
"medicationId": "<id:7>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T18:04:00.000Z"
}
],
"MedicationDocumentLink": [
"EncounterVaccinationLink": [
{
"id": "<id:124>",
"medicationId": "<id:7>",
"documentId": "<id:65>",
"encounterId": "<id:69>",
"vaccinationId": "<id:75>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T19:44:00.000Z"
}
],
"MedicationConditionLink": [
"MedicationDocumentLink": [
{
"id": "<id:125>",
"medicationId": "<id:7>",
"episodeId": "<id:32>",
"documentId": "<id:65>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T21:24:00.000Z"
}
],
"AccountGrant": [
"MedicationConditionLink": [
{
"id": "<id:126>",
"medicationId": "<id:7>",
"episodeId": "<id:32>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T23:04:00.000Z"
}
],
"AccountGrant": [
{
"id": "<id:127>",
"grantorId": "round-trip-owner",
"granteeId": "parity-delegate",
"access": "READ",
@@ -3444,7 +3458,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:00.000Z"
"createdAt": "2026-05-06T06:27:00.000Z"
},
{
"id": "<id:21>",
@@ -3459,7 +3473,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": 2,
"createdAt": "2026-05-06T04:47:02.000Z"
"createdAt": "2026-05-06T06:27:02.000Z"
},
{
"id": "<id:22>",
@@ -3474,14 +3488,14 @@
],
"source": "MANUAL",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:01.000Z"
"createdAt": "2026-05-06T06:27:01.000Z"
}
],
"efficacyTargets": [
{
"id": "<id:23>",
"createdAt": "2026-05-06T06:25:00.000Z",
"updatedAt": "2026-05-06T06:26:00.000Z",
"createdAt": "2026-05-06T08:05:00.000Z",
"updatedAt": "2026-05-06T08:06:00.000Z",
"measurementType": "WEIGHT",
"biomarkerName": "Ferritin",
"primary": false
@@ -3877,7 +3891,7 @@
"name": "Ferr.",
"source": "IMPORT_CHOICE",
"id": "<id:40>",
"createdAt": "2026-05-06T11:26:00.000Z"
"createdAt": "2026-05-06T13:06:00.000Z"
}
]
}
@@ -3890,7 +3904,7 @@
"sourceBiomarkerId": "merged-away-marker",
"sourceName": "Ferritin (Serum)",
"readingCount": 3,
"mergedAt": "2026-05-06T13:06:00.000Z"
"mergedAt": "2026-05-06T14:46:00.000Z"
}
],
"illnessEpisodes": [
@@ -4099,8 +4113,8 @@
"sourceSystem": "PAPERLESS",
"sourceId": "4711",
"sourceInstance": "https://paperless.example.test",
"createdAt": "2026-05-04T01:24:00.000Z",
"updatedAt": "2026-05-04T01:25:00.000Z",
"createdAt": "2026-05-04T01:25:00.000Z",
"updatedAt": "2026-05-04T01:26:00.000Z",
"contentEncrypted": "<sealed:v1:bytes:cm91bmQtdHJpcCBkb2N1bWVudCBieXRlcw==>",
"contentSha256": "0a738470f0945033965858e9895ee721ce981a25f013514d587d0a7a1946be7a",
"contentCodec": "binary2",
@@ -4111,7 +4125,8 @@
"summaryState": "PENDING",
"lastIndexAttemptAt": "2026-06-30T12:34:56.789Z",
"lastIndexOutcome": "rt-InboundDocument-lastIndexOutcome",
"aiReadDeferred": true
"aiReadDeferred": true,
"extractionIncomplete": true
}
],
"manifest": {
@@ -4361,8 +4376,8 @@
],
"onboardingRecord": {
"id": "<id:65>",
"createdAt": "2026-05-06T09:47:00.000Z",
"updatedAt": "2026-05-06T09:48:00.000Z",
"createdAt": "2026-05-06T11:27:00.000Z",
"updatedAt": "2026-05-06T11:28:00.000Z",
"needs": {
"recordTarget": "me",
"areas": [
@@ -4895,6 +4910,14 @@
"updatedAt": "2026-05-05T18:52:00.000Z"
}
],
"documentImportKeys": [
{
"sourceSystem": "PAPRA",
"sourceId": "doc_deleted",
"sourceInstance": "https://papra.example",
"createdAt": "2026-07-03T09:00:00.000Z"
}
],
"personalRecords": [
{
"metricType": "WALKING_RUNNING_DISTANCE",
@@ -4906,7 +4929,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-5km",
"createdAt": "2026-05-05T20:31:01.000Z"
"createdAt": "2026-05-05T22:11:01.000Z"
},
{
"metricType": "WALKING_RUNNING_DISTANCE",
@@ -4918,7 +4941,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-10km",
"createdAt": "2026-05-05T20:31:02.000Z"
"createdAt": "2026-05-05T22:11:02.000Z"
},
{
"metricType": "WEIGHT",
@@ -4930,19 +4953,19 @@
"sourceMeasurementId": "<id:4>",
"source": "MANUAL",
"externalId": null,
"createdAt": "2026-05-05T20:31:00.000Z"
"createdAt": "2026-05-05T22:11:00.000Z"
}
],
"userAchievements": [
{
"achievementId": "retired-badge-from-an-older-release",
"unlockedAt": "2025-11-05T08:00:00.000Z",
"createdAt": "2026-05-05T22:04:01.000Z"
"createdAt": "2026-05-05T23:44:01.000Z"
},
{
"achievementId": "intake-total-10",
"unlockedAt": "2026-02-21T10:15:00.000Z",
"createdAt": "2026-05-05T22:04:00.000Z"
"createdAt": "2026-05-05T23:44:00.000Z"
}
],
"environmentContexts": [
@@ -4988,8 +5011,8 @@
"aqHours": 3,
"aqFetchedAt": "2026-06-30T12:34:56.789Z",
"fetchedAt": "2026-06-16T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:00.000Z",
"updatedAt": "2026-05-06T00:21:00.000Z"
"createdAt": "2026-05-06T02:00:00.000Z",
"updatedAt": "2026-05-06T02:01:00.000Z"
},
{
"date": "2026-07-01",
@@ -5033,8 +5056,8 @@
"aqHours": null,
"aqFetchedAt": null,
"fetchedAt": "2026-07-02T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:01.000Z",
"updatedAt": "2026-05-06T00:21:01.000Z"
"createdAt": "2026-05-06T02:00:01.000Z",
"updatedAt": "2026-05-06T02:01:01.000Z"
}
],
"environmentTravelLocations": [
@@ -5045,8 +5068,8 @@
"lon": null,
"label": null,
"locationEncrypted": "<sealed:v1:{\"lat\":41.4,\"lon\":2.2,\"label\":\"Barcelona\"}>",
"createdAt": "2026-05-06T01:25:00.000Z",
"updatedAt": "2026-05-06T01:26:00.000Z"
"createdAt": "2026-05-06T03:05:00.000Z",
"updatedAt": "2026-05-06T03:06:00.000Z"
}
],
"dawarichDays": [
@@ -5061,8 +5084,8 @@
"countryCodeEncrypted": "<sealed:v1:country:ES>",
"isTravelDay": true,
"fetchedAt": "2026-06-16T03:20:00.000Z",
"createdAt": "2026-05-06T03:11:00.000Z",
"updatedAt": "2026-05-06T03:12:00.000Z"
"createdAt": "2026-05-06T04:51:00.000Z",
"updatedAt": "2026-05-06T04:52:00.000Z"
}
],
"ecgRecordings": [
@@ -5079,8 +5102,8 @@
"averageHeartRate": 88,
"rhythmClassification": "IRREGULAR",
"measurementId": "<id:5>",
"createdAt": "2026-05-06T08:13:00.000Z",
"updatedAt": "2026-05-06T08:14:00.000Z"
"createdAt": "2026-05-06T09:53:00.000Z",
"updatedAt": "2026-05-06T09:54:00.000Z"
}
],
"symptomDefinitions": [
@@ -5176,35 +5199,35 @@
{
"labResultId": "<id:37>",
"documentId": "<id:53>",
"createdAt": "2026-05-06T14:44:00.000Z"
"createdAt": "2026-05-06T16:24:00.000Z"
}
],
"encounterMedicationLinks": [
{
"encounterId": "<id:68>",
"medicationId": "<id:6>",
"createdAt": "2026-05-06T16:24:00.000Z"
"createdAt": "2026-05-06T18:04:00.000Z"
}
],
"encounterVaccinationLinks": [
{
"encounterId": "<id:68>",
"vaccinationId": "<id:73>",
"createdAt": "2026-05-06T18:04:00.000Z"
"createdAt": "2026-05-06T19:44:00.000Z"
}
],
"medicationDocumentLinks": [
{
"medicationId": "<id:6>",
"documentId": "<id:53>",
"createdAt": "2026-05-06T19:44:00.000Z"
"createdAt": "2026-05-06T21:24:00.000Z"
}
],
"medicationConditionLinks": [
{
"medicationId": "<id:6>",
"episodeId": "<id:44>",
"createdAt": "2026-05-06T21:24:00.000Z"
"createdAt": "2026-05-06T23:04:00.000Z"
}
],
"nutrientDays": [
@@ -27,6 +27,7 @@
"documents": 0,
"documentConditionLinks": 1,
"extractedFacts": 1,
"documentImportKeys": 1,
"healthProfile": 1,
"healthProfileFactRevisions": 0,
"customMetrics": 1,
@@ -129,6 +130,7 @@
"documents": 0,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 0,
"healthProfile": 0,
"healthProfileFactRevisions": 0,
"customMetrics": 0,
@@ -345,6 +347,7 @@
"documents": 0,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 0,
"healthProfile": 0,
"healthProfileFactRevisions": 0,
"customMetrics": 0,
@@ -1756,10 +1759,20 @@
"ConsentReceipt": [],
"DocumentConditionLink": [],
"ExtractedFact": [],
"PersonalRecord": [
"DocumentImportKey": [
{
"id": "<id:69>",
"userId": "round-trip-owner",
"sourceSystem": "PAPRA",
"sourceId": "doc_deleted",
"sourceInstance": "https://papra.example",
"createdAt": "2026-07-03T09:00:00.000Z"
}
],
"PersonalRecord": [
{
"id": "<id:70>",
"userId": "round-trip-owner",
"metricType": "WALKING_RUNNING_DISTANCE",
"metricSlot": "running_10km_time",
"direction": "MIN",
@@ -1769,10 +1782,10 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-10km",
"createdAt": "2026-05-05T20:31:02.000Z"
"createdAt": "2026-05-05T22:11:02.000Z"
},
{
"id": "<id:70>",
"id": "<id:71>",
"userId": "round-trip-owner",
"metricType": "WALKING_RUNNING_DISTANCE",
"metricSlot": "running_5km_time",
@@ -1783,10 +1796,10 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-5km",
"createdAt": "2026-05-05T20:31:01.000Z"
"createdAt": "2026-05-05T22:11:01.000Z"
},
{
"id": "<id:71>",
"id": "<id:72>",
"userId": "round-trip-owner",
"metricType": "WEIGHT",
"metricSlot": null,
@@ -1797,28 +1810,28 @@
"sourceMeasurementId": "<id:4>",
"source": "MANUAL",
"externalId": null,
"createdAt": "2026-05-05T20:31:00.000Z"
"createdAt": "2026-05-05T22:11:00.000Z"
}
],
"UserAchievement": [
{
"id": "<id:72>",
"id": "<id:73>",
"userId": "round-trip-owner",
"achievementId": "intake-total-10",
"unlockedAt": "2026-02-21T10:15:00.000Z",
"createdAt": "2026-05-05T22:04:00.000Z"
"createdAt": "2026-05-05T23:44:00.000Z"
},
{
"id": "<id:73>",
"id": "<id:74>",
"userId": "round-trip-owner",
"achievementId": "retired-badge-from-an-older-release",
"unlockedAt": "2025-11-05T08:00:00.000Z",
"createdAt": "2026-05-05T22:04:01.000Z"
"createdAt": "2026-05-05T23:44:01.000Z"
}
],
"EnvironmentContext": [
{
"id": "<id:74>",
"id": "<id:75>",
"userId": "round-trip-owner",
"date": "2026-06-15",
"locationEncrypted": "<sealed:v2:{\"lat\":41.4,\"lon\":2.2,\"label\":\"Barcelona\"}>",
@@ -1858,11 +1871,11 @@
"aqHours": 3,
"aqFetchedAt": "2026-06-30T12:34:56.789Z",
"fetchedAt": "2026-06-16T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:00.000Z",
"updatedAt": "2026-05-06T00:21:00.000Z"
"createdAt": "2026-05-06T02:00:00.000Z",
"updatedAt": "2026-05-06T02:01:00.000Z"
},
{
"id": "<id:75>",
"id": "<id:76>",
"userId": "round-trip-owner",
"date": "2026-07-01",
"locationEncrypted": "<sealed:v2:{\"lat\":52.5,\"lon\":13.4,\"label\":\"Berlin\"}>",
@@ -1902,24 +1915,24 @@
"aqHours": null,
"aqFetchedAt": null,
"fetchedAt": "2026-07-02T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:01.000Z",
"updatedAt": "2026-05-06T00:21:01.000Z"
"createdAt": "2026-05-06T02:00:01.000Z",
"updatedAt": "2026-05-06T02:01:01.000Z"
}
],
"EnvironmentTravelLocation": [
{
"id": "<id:76>",
"id": "<id:77>",
"userId": "round-trip-owner",
"startDate": "2026-06-10",
"endDate": "2026-06-20",
"locationEncrypted": "<sealed:v2:{\"lat\":41.4,\"lon\":2.2,\"label\":\"Barcelona\"}>",
"createdAt": "2026-05-06T01:25:00.000Z",
"updatedAt": "2026-05-06T01:26:00.000Z"
"createdAt": "2026-05-06T03:05:00.000Z",
"updatedAt": "2026-05-06T03:06:00.000Z"
}
],
"DawarichDay": [
{
"id": "<id:77>",
"id": "<id:78>",
"userId": "round-trip-owner",
"date": "2026-06-15",
"totalDistanceM": 48300,
@@ -1930,13 +1943,13 @@
"countryCodeEncrypted": "<sealed:v2:country:ES>",
"isTravelDay": true,
"fetchedAt": "2026-06-16T03:20:00.000Z",
"createdAt": "2026-05-06T03:11:00.000Z",
"updatedAt": "2026-05-06T03:12:00.000Z"
"createdAt": "2026-05-06T04:51:00.000Z",
"updatedAt": "2026-05-06T04:52:00.000Z"
}
],
"MedicationScheduleRevision": [
{
"id": "<id:78>",
"id": "<id:79>",
"medicationId": "<id:6>",
"validFrom": "2026-01-01T00:00:00.000Z",
"validUntil": "2026-03-01T00:00:00.000Z",
@@ -1949,10 +1962,10 @@
],
"source": "ARCHIVED",
"supersededByRevisionId": null,
"createdAt": "2026-05-06T04:47:00.000Z"
"createdAt": "2026-05-06T06:27:00.000Z"
},
{
"id": "<id:79>",
"id": "<id:80>",
"medicationId": "<id:6>",
"validFrom": "2026-02-01T00:00:00.000Z",
"validUntil": "2026-03-01T00:00:00.000Z",
@@ -1964,11 +1977,11 @@
}
],
"source": "ARCHIVED",
"supersededByRevisionId": "<id:80>",
"createdAt": "2026-05-06T04:47:02.000Z"
"supersededByRevisionId": "<id:81>",
"createdAt": "2026-05-06T06:27:02.000Z"
},
{
"id": "<id:80>",
"id": "<id:81>",
"medicationId": "<id:6>",
"validFrom": "2026-03-01T00:00:00.000Z",
"validUntil": "2026-05-01T00:00:00.000Z",
@@ -1981,12 +1994,12 @@
],
"source": "MANUAL",
"supersededByRevisionId": null,
"createdAt": "2026-05-06T04:47:01.000Z"
"createdAt": "2026-05-06T06:27:01.000Z"
}
],
"MedicationEfficacyTarget": [
{
"id": "<id:81>",
"id": "<id:82>",
"medicationId": "<id:6>",
"measurementType": "WEIGHT",
"biomarkerId": "<id:23>",
@@ -1997,7 +2010,7 @@
],
"EcgRecording": [
{
"id": "<id:82>",
"id": "<id:83>",
"userId": "round-trip-owner",
"source": "MANUAL",
"externalRecordingId": "round-trip-signal-1",
@@ -2010,13 +2023,13 @@
"averageHeartRate": 88,
"rhythmClassification": "IRREGULAR",
"measurementId": "<id:2>",
"createdAt": "2026-05-06T08:13:00.000Z",
"updatedAt": "2026-05-06T08:14:00.000Z"
"createdAt": "2026-05-06T09:53:00.000Z",
"updatedAt": "2026-05-06T09:54:00.000Z"
}
],
"OnboardingRecord": [
{
"id": "<id:83>",
"id": "<id:84>",
"userId": "round-trip-owner",
"needsJson": {
"areas": [
@@ -2085,7 +2098,7 @@
],
"BiomarkerAlias": [
{
"id": "<id:84>",
"id": "<id:85>",
"userId": "round-trip-owner",
"biomarkerId": "<id:23>",
"name": "Ferr.",
@@ -2096,47 +2109,47 @@
],
"BiomarkerMerge": [
{
"id": "<id:85>",
"id": "<id:86>",
"userId": "round-trip-owner",
"targetBiomarkerId": "<id:23>",
"sourceBiomarkerId": "merged-away-marker",
"sourceName": "Ferritin (Serum)",
"readingCount": 3,
"mergedAt": "2026-05-06T13:06:00.000Z"
"mergedAt": "2026-05-06T14:46:00.000Z"
}
],
"LabResultDocumentLink": [],
"EncounterMedicationLink": [
{
"id": "<id:86>",
"id": "<id:87>",
"encounterId": "<id:49>",
"medicationId": "<id:6>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T16:24:00.000Z"
"createdAt": "2026-05-06T18:04:00.000Z"
}
],
"EncounterVaccinationLink": [
{
"id": "<id:87>",
"id": "<id:88>",
"encounterId": "<id:49>",
"vaccinationId": "<id:52>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T18:04:00.000Z"
"createdAt": "2026-05-06T19:44:00.000Z"
}
],
"MedicationDocumentLink": [],
"MedicationConditionLink": [
{
"id": "<id:88>",
"id": "<id:89>",
"medicationId": "<id:6>",
"episodeId": "<id:26>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T21:24:00.000Z"
"createdAt": "2026-05-06T23:04:00.000Z"
}
],
"AccountGrant": [
{
"id": "<id:89>",
"id": "<id:90>",
"grantorId": "round-trip-owner",
"granteeId": "parity-delegate",
"access": "READ",
@@ -2745,7 +2758,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:00.000Z"
"createdAt": "2026-05-06T06:27:00.000Z"
},
{
"validFrom": "2026-02-01T00:00:00.000Z",
@@ -2759,7 +2772,7 @@
],
"source": "ARCHIVED",
"supersededByIndex": 2,
"createdAt": "2026-05-06T04:47:02.000Z"
"createdAt": "2026-05-06T06:27:02.000Z"
},
{
"validFrom": "2026-03-01T00:00:00.000Z",
@@ -2773,7 +2786,7 @@
],
"source": "MANUAL",
"supersededByIndex": null,
"createdAt": "2026-05-06T04:47:01.000Z"
"createdAt": "2026-05-06T06:27:01.000Z"
}
],
"efficacyTargets": [
@@ -2976,7 +2989,7 @@
"sourceBiomarkerId": "merged-away-marker",
"sourceName": "Ferritin (Serum)",
"readingCount": 3,
"mergedAt": "2026-05-06T13:06:00.000Z"
"mergedAt": "2026-05-06T14:46:00.000Z"
}
],
"illnessEpisodes": [
@@ -3551,6 +3564,14 @@
"consentReceipts": [],
"documentConditionLinks": [],
"extractedFacts": [],
"documentImportKeys": [
{
"sourceSystem": "PAPRA",
"sourceId": "doc_deleted",
"sourceInstance": "https://papra.example",
"createdAt": "2026-07-03T09:00:00.000Z"
}
],
"personalRecords": [
{
"metricType": "WALKING_RUNNING_DISTANCE",
@@ -3562,7 +3583,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-5km",
"createdAt": "2026-05-05T20:31:01.000Z"
"createdAt": "2026-05-05T22:11:01.000Z"
},
{
"metricType": "WALKING_RUNNING_DISTANCE",
@@ -3574,7 +3595,7 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-10km",
"createdAt": "2026-05-05T20:31:02.000Z"
"createdAt": "2026-05-05T22:11:02.000Z"
},
{
"metricType": "WEIGHT",
@@ -3586,19 +3607,19 @@
"sourceMeasurementId": "<id:3>",
"source": "MANUAL",
"externalId": null,
"createdAt": "2026-05-05T20:31:00.000Z"
"createdAt": "2026-05-05T22:11:00.000Z"
}
],
"userAchievements": [
{
"achievementId": "retired-badge-from-an-older-release",
"unlockedAt": "2025-11-05T08:00:00.000Z",
"createdAt": "2026-05-05T22:04:01.000Z"
"createdAt": "2026-05-05T23:44:01.000Z"
},
{
"achievementId": "intake-total-10",
"unlockedAt": "2026-02-21T10:15:00.000Z",
"createdAt": "2026-05-05T22:04:00.000Z"
"createdAt": "2026-05-05T23:44:00.000Z"
}
],
"environmentContexts": [
@@ -3643,8 +3664,8 @@
"aqHours": 3,
"aqFetchedAt": "2026-06-30T12:34:56.789Z",
"fetchedAt": "2026-06-16T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:00.000Z",
"updatedAt": "2026-05-06T00:21:00.000Z"
"createdAt": "2026-05-06T02:00:00.000Z",
"updatedAt": "2026-05-06T02:01:00.000Z"
},
{
"date": "2026-07-01",
@@ -3687,8 +3708,8 @@
"aqHours": null,
"aqFetchedAt": null,
"fetchedAt": "2026-07-02T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:01.000Z",
"updatedAt": "2026-05-06T00:21:01.000Z"
"createdAt": "2026-05-06T02:00:01.000Z",
"updatedAt": "2026-05-06T02:01:01.000Z"
}
],
"environmentTravelLocations": [
@@ -3698,8 +3719,8 @@
"lat": 41.4,
"lon": 2.2,
"label": "Barcelona",
"createdAt": "2026-05-06T01:25:00.000Z",
"updatedAt": "2026-05-06T01:26:00.000Z"
"createdAt": "2026-05-06T03:05:00.000Z",
"updatedAt": "2026-05-06T03:06:00.000Z"
}
],
"dawarichDays": [
@@ -3713,8 +3734,8 @@
"countryCode": "ES",
"isTravelDay": true,
"fetchedAt": "2026-06-16T03:20:00.000Z",
"createdAt": "2026-05-06T03:11:00.000Z",
"updatedAt": "2026-05-06T03:12:00.000Z"
"createdAt": "2026-05-06T04:51:00.000Z",
"updatedAt": "2026-05-06T04:52:00.000Z"
}
],
"ecgRecordings": [
@@ -3739,8 +3760,8 @@
"averageHeartRate": 88,
"rhythmClassification": "IRREGULAR",
"measurementId": "<id:4>",
"createdAt": "2026-05-06T08:13:00.000Z",
"updatedAt": "2026-05-06T08:14:00.000Z"
"createdAt": "2026-05-06T09:53:00.000Z",
"updatedAt": "2026-05-06T09:54:00.000Z"
}
],
"symptomDefinitions": [
@@ -3816,14 +3837,14 @@
{
"encounterId": "<id:16>",
"medicationId": "<id:5>",
"createdAt": "2026-05-06T16:24:00.000Z"
"createdAt": "2026-05-06T18:04:00.000Z"
}
],
"encounterVaccinationLinks": [
{
"encounterId": "<id:16>",
"vaccinationId": "<id:19>",
"createdAt": "2026-05-06T18:04:00.000Z"
"createdAt": "2026-05-06T19:44:00.000Z"
}
],
"medicationDocumentLinks": [],
@@ -3831,7 +3852,7 @@
{
"medicationId": "<id:5>",
"episodeId": "<id:11>",
"createdAt": "2026-05-06T21:24:00.000Z"
"createdAt": "2026-05-06T23:04:00.000Z"
}
],
"nutrientDays": [
@@ -27,6 +27,7 @@
"documents": 0,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 0,
"healthProfile": 0,
"healthProfileFactRevisions": 0,
"customMetrics": 0,
@@ -144,6 +145,7 @@
"documents": 0,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 0,
"healthProfile": 0,
"healthProfileFactRevisions": 0,
"customMetrics": 0,
@@ -360,6 +362,7 @@
"documents": 0,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 0,
"healthProfile": 0,
"healthProfileFactRevisions": 0,
"customMetrics": 0,
@@ -737,6 +740,7 @@
"ConsentReceipt": [],
"DocumentConditionLink": [],
"ExtractedFact": [],
"DocumentImportKey": [],
"PersonalRecord": [],
"UserAchievement": [],
"EnvironmentContext": [],
@@ -27,6 +27,7 @@
"documents": 0,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 0,
"healthProfile": 0,
"healthProfileFactRevisions": 0,
"customMetrics": 0,
@@ -83,6 +84,7 @@
"documents": 0,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 0,
"healthProfile": 0,
"healthProfileFactRevisions": 0,
"customMetrics": 0,
@@ -299,6 +301,7 @@
"documents": 0,
"documentConditionLinks": 0,
"extractedFacts": 0,
"documentImportKeys": 0,
"healthProfile": 0,
"healthProfileFactRevisions": 0,
"customMetrics": 0,
@@ -912,6 +915,7 @@
"ConsentReceipt": [],
"DocumentConditionLink": [],
"ExtractedFact": [],
"DocumentImportKey": [],
"PersonalRecord": [],
"UserAchievement": [],
"EnvironmentContext": [],
@@ -1800,6 +1804,7 @@
"consentReceipts": [],
"documentConditionLinks": [],
"extractedFacts": [],
"documentImportKeys": [],
"personalRecords": [],
"userAchievements": [],
"environmentContexts": [],
@@ -1239,8 +1239,9 @@
"sourceId": "4711",
"sourceInstance": "https://paperless.example.test",
"aiReadDeferred": true,
"createdAt": "2026-05-04T01:24:00.000Z",
"updatedAt": "2026-05-04T01:25:00.000Z",
"extractionIncomplete": true,
"createdAt": "2026-05-04T01:25:00.000Z",
"updatedAt": "2026-05-04T01:26:00.000Z",
"deletedAt": null
},
{
@@ -1268,8 +1269,9 @@
"sourceId": null,
"sourceInstance": null,
"aiReadDeferred": false,
"createdAt": "2026-05-04T01:24:01.000Z",
"updatedAt": "2026-05-04T01:25:01.000Z",
"extractionIncomplete": false,
"createdAt": "2026-05-04T01:25:01.000Z",
"updatedAt": "2026-05-04T01:26:01.000Z",
"deletedAt": "<tombstone>"
}
],
@@ -1872,10 +1874,20 @@
"updatedAt": "2026-05-05T18:52:00.000Z"
}
],
"PersonalRecord": [
"DocumentImportKey": [
{
"id": "<id:105>",
"userId": "round-trip-owner",
"sourceSystem": "PAPRA",
"sourceId": "doc_deleted",
"sourceInstance": "https://papra.example",
"createdAt": "2026-07-03T09:00:00.000Z"
}
],
"PersonalRecord": [
{
"id": "<id:106>",
"userId": "round-trip-owner",
"metricType": "WALKING_RUNNING_DISTANCE",
"metricSlot": "running_10km_time",
"direction": "MIN",
@@ -1885,10 +1897,10 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-10km",
"createdAt": "2026-05-05T20:31:02.000Z"
"createdAt": "2026-05-05T22:11:02.000Z"
},
{
"id": "<id:106>",
"id": "<id:107>",
"userId": "round-trip-owner",
"metricType": "WALKING_RUNNING_DISTANCE",
"metricSlot": "running_5km_time",
@@ -1899,10 +1911,10 @@
"sourceMeasurementId": null,
"source": "APPLE_HEALTH",
"externalId": "workout-pr-5km",
"createdAt": "2026-05-05T20:31:01.000Z"
"createdAt": "2026-05-05T22:11:01.000Z"
},
{
"id": "<id:107>",
"id": "<id:108>",
"userId": "round-trip-owner",
"metricType": "WEIGHT",
"metricSlot": null,
@@ -1913,28 +1925,28 @@
"sourceMeasurementId": "<id:5>",
"source": "MANUAL",
"externalId": null,
"createdAt": "2026-05-05T20:31:00.000Z"
"createdAt": "2026-05-05T22:11:00.000Z"
}
],
"UserAchievement": [
{
"id": "<id:108>",
"id": "<id:109>",
"userId": "round-trip-owner",
"achievementId": "intake-total-10",
"unlockedAt": "2026-02-21T10:15:00.000Z",
"createdAt": "2026-05-05T22:04:00.000Z"
"createdAt": "2026-05-05T23:44:00.000Z"
},
{
"id": "<id:109>",
"id": "<id:110>",
"userId": "round-trip-owner",
"achievementId": "retired-badge-from-an-older-release",
"unlockedAt": "2025-11-05T08:00:00.000Z",
"createdAt": "2026-05-05T22:04:01.000Z"
"createdAt": "2026-05-05T23:44:01.000Z"
}
],
"EnvironmentContext": [
{
"id": "<id:110>",
"id": "<id:111>",
"userId": "round-trip-owner",
"date": "2026-06-15",
"locationEncrypted": "<sealed:v1:{\"lat\":41.4,\"lon\":2.2,\"label\":\"Barcelona\"}>",
@@ -1974,11 +1986,11 @@
"aqHours": 3,
"aqFetchedAt": "2026-06-30T12:34:56.789Z",
"fetchedAt": "2026-06-16T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:00.000Z",
"updatedAt": "2026-05-06T00:21:00.000Z"
"createdAt": "2026-05-06T02:00:00.000Z",
"updatedAt": "2026-05-06T02:01:00.000Z"
},
{
"id": "<id:111>",
"id": "<id:112>",
"userId": "round-trip-owner",
"date": "2026-07-01",
"locationEncrypted": "<sealed:v1:{\"lat\":52.5,\"lon\":13.4,\"label\":\"Berlin\"}>",
@@ -2018,24 +2030,24 @@
"aqHours": null,
"aqFetchedAt": null,
"fetchedAt": "2026-07-02T03:15:00.000Z",
"createdAt": "2026-05-06T00:20:01.000Z",
"updatedAt": "2026-05-06T00:21:01.000Z"
"createdAt": "2026-05-06T02:00:01.000Z",
"updatedAt": "2026-05-06T02:01:01.000Z"
}
],
"EnvironmentTravelLocation": [
{
"id": "<id:112>",
"id": "<id:113>",
"userId": "round-trip-owner",
"startDate": "2026-06-10",
"endDate": "2026-06-20",
"locationEncrypted": "<sealed:v1:{\"lat\":41.4,\"lon\":2.2,\"label\":\"Barcelona\"}>",
"createdAt": "2026-05-06T01:25:00.000Z",
"updatedAt": "2026-05-06T01:26:00.000Z"
"createdAt": "2026-05-06T03:05:00.000Z",
"updatedAt": "2026-05-06T03:06:00.000Z"
}
],
"DawarichDay": [
{
"id": "<id:113>",
"id": "<id:114>",
"userId": "round-trip-owner",
"date": "2026-06-15",
"totalDistanceM": 48300,
@@ -2046,13 +2058,13 @@
"countryCodeEncrypted": "<sealed:v1:country:ES>",
"isTravelDay": true,
"fetchedAt": "2026-06-16T03:20:00.000Z",
"createdAt": "2026-05-06T03:11:00.000Z",
"updatedAt": "2026-05-06T03:12:00.000Z"
"createdAt": "2026-05-06T04:51:00.000Z",
"updatedAt": "2026-05-06T04:52:00.000Z"
}
],
"MedicationScheduleRevision": [
{
"id": "<id:114>",
"id": "<id:115>",
"medicationId": "<id:7>",
"validFrom": "2026-01-01T00:00:00.000Z",
"validUntil": "2026-03-01T00:00:00.000Z",
@@ -2065,10 +2077,10 @@
],
"source": "ARCHIVED",
"supersededByRevisionId": null,
"createdAt": "2026-05-06T04:47:00.000Z"
"createdAt": "2026-05-06T06:27:00.000Z"
},
{
"id": "<id:115>",
"id": "<id:116>",
"medicationId": "<id:7>",
"validFrom": "2026-02-01T00:00:00.000Z",
"validUntil": "2026-03-01T00:00:00.000Z",
@@ -2080,11 +2092,11 @@
}
],
"source": "ARCHIVED",
"supersededByRevisionId": "<id:116>",
"createdAt": "2026-05-06T04:47:02.000Z"
"supersededByRevisionId": "<id:117>",
"createdAt": "2026-05-06T06:27:02.000Z"
},
{
"id": "<id:116>",
"id": "<id:117>",
"medicationId": "<id:7>",
"validFrom": "2026-03-01T00:00:00.000Z",
"validUntil": "2026-05-01T00:00:00.000Z",
@@ -2097,23 +2109,23 @@
],
"source": "MANUAL",
"supersededByRevisionId": null,
"createdAt": "2026-05-06T04:47:01.000Z"
"createdAt": "2026-05-06T06:27:01.000Z"
}
],
"MedicationEfficacyTarget": [
{
"id": "<id:117>",
"id": "<id:118>",
"medicationId": "<id:7>",
"measurementType": "WEIGHT",
"biomarkerId": "<id:26>",
"primary": false,
"createdAt": "2026-05-06T06:25:00.000Z",
"updatedAt": "2026-05-06T06:26:00.000Z"
"createdAt": "2026-05-06T08:05:00.000Z",
"updatedAt": "2026-05-06T08:06:00.000Z"
}
],
"EcgRecording": [
{
"id": "<id:118>",
"id": "<id:119>",
"userId": "round-trip-owner",
"source": "MANUAL",
"externalRecordingId": "round-trip-signal-1",
@@ -2126,13 +2138,13 @@
"averageHeartRate": 88,
"rhythmClassification": "IRREGULAR",
"measurementId": "<id:2>",
"createdAt": "2026-05-06T08:13:00.000Z",
"updatedAt": "2026-05-06T08:14:00.000Z"
"createdAt": "2026-05-06T09:53:00.000Z",
"updatedAt": "2026-05-06T09:54:00.000Z"
}
],
"OnboardingRecord": [
{
"id": "<id:119>",
"id": "<id:120>",
"userId": "round-trip-owner",
"needsJson": {
"areas": [
@@ -2171,80 +2183,80 @@
},
"modulesDerivedAt": "2026-07-01T08:00:00.000Z",
"completedAt": "2026-07-01T08:00:00.000Z",
"createdAt": "2026-05-06T09:47:00.000Z",
"updatedAt": "2026-05-06T09:48:00.000Z"
"createdAt": "2026-05-06T11:27:00.000Z",
"updatedAt": "2026-05-06T11:28:00.000Z"
}
],
"BiomarkerAlias": [
{
"id": "<id:120>",
"id": "<id:121>",
"userId": "round-trip-owner",
"biomarkerId": "<id:26>",
"name": "Ferr.",
"nameFolded": "ferr.",
"source": "IMPORT_CHOICE",
"createdAt": "2026-05-06T11:26:00.000Z"
"createdAt": "2026-05-06T13:06:00.000Z"
}
],
"BiomarkerMerge": [
{
"id": "<id:121>",
"id": "<id:122>",
"userId": "round-trip-owner",
"targetBiomarkerId": "<id:26>",
"sourceBiomarkerId": "merged-away-marker",
"sourceName": "Ferritin (Serum)",
"readingCount": 3,
"mergedAt": "2026-05-06T13:06:00.000Z"
"mergedAt": "2026-05-06T14:46:00.000Z"
}
],
"LabResultDocumentLink": [
{
"id": "<id:122>",
"id": "<id:123>",
"labResultId": "<id:25>",
"documentId": "<id:65>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T14:44:00.000Z"
}
],
"EncounterMedicationLink": [
{
"id": "<id:123>",
"encounterId": "<id:70>",
"medicationId": "<id:7>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T16:24:00.000Z"
}
],
"EncounterVaccinationLink": [
"EncounterMedicationLink": [
{
"id": "<id:124>",
"encounterId": "<id:70>",
"vaccinationId": "<id:76>",
"medicationId": "<id:7>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T18:04:00.000Z"
}
],
"MedicationDocumentLink": [
"EncounterVaccinationLink": [
{
"id": "<id:125>",
"medicationId": "<id:7>",
"documentId": "<id:65>",
"encounterId": "<id:70>",
"vaccinationId": "<id:76>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T19:44:00.000Z"
}
],
"MedicationConditionLink": [
"MedicationDocumentLink": [
{
"id": "<id:126>",
"medicationId": "<id:7>",
"episodeId": "<id:32>",
"documentId": "<id:65>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T21:24:00.000Z"
}
],
"AccountGrant": [
"MedicationConditionLink": [
{
"id": "<id:127>",
"medicationId": "<id:7>",
"episodeId": "<id:32>",
"userId": "round-trip-owner",
"createdAt": "2026-05-06T23:04:00.000Z"
}
],
"AccountGrant": [
{
"id": "<id:128>",
"grantorId": "round-trip-owner",
"granteeId": "parity-delegate",
"access": "WRITE",
@@ -2253,13 +2265,13 @@
"measurements"
]
},
"invitedAt": "2026-05-07T02:25:00.000Z",
"invitedAt": "2026-05-07T04:05:00.000Z",
"acceptedAt": "2026-07-02T09:00:00.000Z",
"revokedAt": null,
"revokedBy": null,
"expiresAt": null,
"lastUsedAt": null,
"createdAt": "2026-05-07T02:31:00.000Z"
"createdAt": "2026-05-07T04:11:00.000Z"
}
],
"User": {
@@ -2271,7 +2283,7 @@
"oidcIssuer": "rt-User-oidcIssuer",
"oidcSub": "rt-User-oidcSub",
"createdAt": "2026-06-30T12:34:56.789Z",
"updatedAt": "2026-05-06T23:08:00.000Z",
"updatedAt": "2026-05-07T00:48:00.000Z",
"managedProfileAt": "2026-06-30T12:34:56.789Z",
"heightCm": 2.5,
"dateOfBirth": "2026-06-30T12:34:56.789Z",
@@ -0,0 +1,208 @@
/**
* The memory that the person deleted an imported document, through a backup
* and a restore onto an instance that has never seen the account.
*
* Deleting an imported document leaves its source keys behind: on the
* tombstone while it waits in Recently deleted, then in the import ledger
* (`DocumentImportKey`) once the purge removes it. An importer re-sending
* such a key is answered "deleted" and nothing is stored. The backup carried
* neither, so after a restore onto a fresh instance a token importer brought
* back every document the person had deleted on purpose.
*
* The file now carries the ledger, and the keys of the documents waiting in
* Recently deleted as ledger entries (the documents themselves stay out, as
* they always have): restored, both are answered "deleted", while a live
* document's key stays the live document's.
*/
import { beforeEach, describe, expect, it, vi } from "vitest";
process.env.ENCRYPTION_KEY =
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
import { encrypt } from "@/lib/crypto";
import { encryptDocumentContent } from "@/lib/documents/store";
import { buildFullBackupPayload } from "@/lib/export/full-backup-payload";
import { parseBackupPayload } from "@/lib/validations/backup";
import { POST as RESTORE } from "./restore-job-driver";
import { cookieJar, headerJar } from "./mock-next-headers";
import { getPrismaClient, truncateAllTables } from "./setup";
vi.mock("next/headers", async () => {
const { cookieJar, headerJar } = await import("./mock-next-headers");
return {
headers: vi.fn(async () => ({
get: (name: string) => headerJar.get(name.toLowerCase()) ?? null,
})),
cookies: vi.fn(async () => ({
get: (name: string) => {
const value = cookieJar.get(name);
return value ? { name, value } : undefined;
},
set: (name: string, value: string) => cookieJar.set(name, value),
delete: (name: string) => cookieJar.delete(name),
})),
};
});
vi.mock("@/lib/db-compat", () => ({
ensureDbCompatibility: vi.fn().mockResolvedValue(undefined),
}));
vi.mock("@/lib/cache/invalidate", async (importOriginal) => {
const actual =
await importOriginal<typeof import("@/lib/cache/invalidate")>();
return { ...actual, invalidateUserData: vi.fn() };
});
const INSTANCE = "https://paperless.example";
const pdf = (tag: string) =>
Buffer.from(`%PDF-1.7\n% ${tag}\n1 0 obj\n<<>>\nendobj\n%%EOF\n`);
beforeEach(async () => {
await truncateAllTables(getPrismaClient());
cookieJar.clear();
headerJar.clear();
});
async function seedDocument(
userId: string,
sourceId: string,
deletedAt: Date | null,
) {
const bytes = pdf(sourceId);
const { content, codec } = encryptDocumentContent(bytes);
return getPrismaClient().inboundDocument.create({
data: {
userId,
mimeType: "application/pdf",
byteSize: bytes.byteLength,
contentEncrypted: content,
contentCodec: codec,
sourceSystem: "PAPERLESS",
sourceId,
sourceInstance: INSTANCE,
deletedAt,
},
});
}
async function signIn(userId: string) {
const session = await getPrismaClient().session.create({
data: { userId, expiresAt: new Date(Date.now() + 600_000) },
});
cookieJar.set("healthlog_session", session.id);
}
async function importAgain(sourceId: string) {
const { POST } = await import("@/app/api/documents/inbound/route");
const bytes = pdf(sourceId);
const payload = new Uint8Array(bytes.byteLength);
payload.set(bytes);
const form = new FormData();
form.append("file", new Blob([payload]), "letter.pdf");
form.append("sourceSystem", "PAPERLESS");
form.append("sourceId", sourceId);
form.append("sourceInstance", INSTANCE);
const res = await (POST as unknown as (r: Request) => Promise<Response>)(
new Request("http://localhost/api/documents/inbound", {
method: "POST",
body: form,
}),
);
return { status: res.status, body: await res.json() };
}
describe("a restore onto a fresh instance", () => {
it("still answers a deliberately deleted import as deleted", async () => {
const prisma = getPrismaClient();
const owner = await prisma.user.create({
data: {
username: "ledger-owner",
email: "ledger-owner@example.test",
role: "ADMIN",
modulePreferencesJson: { inboundDocuments: true },
},
});
// Waiting in Recently deleted, with a second key remembered as an alias.
const waiting = await seedDocument(owner.id, "412", new Date());
await prisma.documentSourceAlias.create({
data: {
userId: owner.id,
documentId: waiting.id,
sourceSystem: "PAPERLESS",
sourceId: "413",
sourceInstance: INSTANCE,
},
});
// Purged already: only the ledger remembers it.
await prisma.documentImportKey.create({
data: {
userId: owner.id,
sourceSystem: "PAPERLESS",
sourceId: "500",
sourceInstance: INSTANCE,
},
});
// Live.
await seedDocument(owner.id, "600", null);
const { payload: built } = await buildFullBackupPayload(prisma, owner.id, {
purpose: "disaster-recovery",
});
const parsed = parseBackupPayload(JSON.stringify(built));
// The fresh instance: the account as the file brings it, nothing else.
await prisma.documentImportKey.deleteMany({ where: { userId: owner.id } });
await prisma.inboundDocument.deleteMany({ where: { userId: owner.id } });
const backup = await prisma.dataBackup.create({
data: {
userId: owner.id,
type: "IMPORT_LEDGER_ROUND_TRIP",
data: encrypt(JSON.stringify(parsed)),
},
});
await signIn(owner.id);
const response = await RESTORE(
new Request(`http://localhost/api/admin/backups/${backup.id}/restore`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ confirm: "RESTORE" }),
}) as unknown as Parameters<typeof RESTORE>[0],
{ params: Promise.resolve({ id: backup.id }) },
);
expect(response.status).toBe(200);
expect(
(
await prisma.documentImportKey.findMany({
where: { userId: owner.id },
select: { sourceSystem: true, sourceId: true, sourceInstance: true },
orderBy: { sourceId: "asc" },
})
).map((k) => [k.sourceSystem, k.sourceId, k.sourceInstance]),
).toEqual([
["PAPERLESS", "412", INSTANCE],
["PAPERLESS", "413", INSTANCE],
["PAPERLESS", "500", INSTANCE],
]);
await signIn(owner.id);
for (const sourceId of ["412", "413", "500"]) {
const again = await importAgain(sourceId);
expect(again.status, sourceId).toBe(200);
expect(again.body.meta, sourceId).toEqual({
duplicate: true,
deleted: true,
});
}
// The live document's key is still the live document's.
const live = await importAgain("600");
expect(live.status).toBe(200);
expect(live.body.meta).toMatchObject({ duplicate: true });
expect(live.body.meta.deleted).toBeUndefined();
expect(
await prisma.inboundDocument.count({ where: { userId: owner.id } }),
).toBe(1);
});
});
+13
View File
@@ -847,6 +847,19 @@ export async function seedEveryTwoEndedModel(
},
});
// An imported page the person deleted and the purge then removed: only the
// ledger remembers it, and a restore that dropped the row would let the
// importer store it again.
await prisma.documentImportKey.create({
data: {
userId: OWNER_ID,
sourceSystem: "PAPRA",
sourceId: "doc_deleted",
sourceInstance: "https://papra.example",
createdAt: AT("2026-07-03T09:00:00.000Z"),
},
});
// The page filed under the SECOND condition, not the first. A restore that
// kept the count and lost the pairing would leave the vault sorted wrongly
// rather than visibly unsorted, which is harder to notice and worse.
@@ -286,6 +286,8 @@ const COUNT_BACK: Record<
DocumentConditionLink: (p, userId) =>
p.documentConditionLink.count({ where: { userId } }),
ExtractedFact: (p, userId) => p.extractedFact.count({ where: { userId } }),
DocumentImportKey: (p, userId) =>
p.documentImportKey.count({ where: { userId } }),
PersonalRecord: (p, userId) => p.personalRecord.count({ where: { userId } }),
UserAchievement: (p, userId) =>
p.userAchievement.count({ where: { userId } }),