Data export and deletion
Export bundle shape, deletion semantics, audit-chain tombstones, and how the SDK maps to GDPR right-to-access and right-to-erasure.
Updated May 29, 2026
Patients own their data. Nyra exposes two lifecycle calls behind the patient principal: export and deletion. Agents can drive both on a patient's behalf, but only when the request is initiated by that patient or by a clinician with explicit scope. The audit chain is the one thing that survives a deletion, in a redacted tombstone form, because the chain is what proves the deletion happened.
Export shape
An export bundle is a single JSON-LD document. It carries the entire projection of a patient's record that the SDK exposes elsewhere, plus the audit chain that proves provenance.
type ExportBundle = {
"@context": "https://nyra.us.com/schemas/export/v1";
schemaVersion: "v1";
exportedAt: string; // ISO 8601
exportedBy: { role: "patient" | "clinician"; principalId: string };
patient: { patientId: string; clinicId: string };
reflections: Reflection[];
scales: ScaleCapture[];
evidenceMaps: EvidenceMap[]; // every projection ever materialized
auditChain: AuditLogEntry[]; // full chain, oldest to newest
notes: ClinicianNote[]; // notes shared with the patient by their clinician
};
schemaVersion is the field to switch on if you process old exports. The bundle is forwards-stable: new fields land additively, and removed fields stay as null for two minor versions before disappearing.
Triggering an export
import { NyraClient } from "@humyn/nyra";
const job = await client.requestExport({ patientId });
job.jobId; // "exp_..."
job.status; // "queued" | "running" | "ready" | "failed"
const ready = await client.pollExport(job.jobId);
if (ready.status === "ready") {
const bundle = await client.fetchExportBundle(ready.downloadUrl);
}
job = await client.request_export(patient_id=patient_id)
ready = await client.poll_export(job.job_id)
if ready.status == "ready":
bundle = await client.fetch_export_bundle(ready.download_url)
Exports are asynchronous because the bundle materializes through the same projection layer the evidence map uses. A small patient takes seconds. A patient with two years of dense reflections takes longer. The download URL is short-lived (15 minutes) and bound to the requesting principal.
Deletion semantics
Deletion is permanent for the patient-facing surfaces and partial for the audit chain. The reflections, evidence maps, drafts, and notes are removed. The audit chain retains a tombstone for each removed entry: same id, same previousHash, same hash, but the summary and any free-text fields are replaced with "redacted". This preserves the chain's integrity so verification still passes, while removing the content the deletion requested.
const req = await client.requestDeletion({
patientId,
reason: "patient-initiated", // or "clinician-initiated", "court-order"
initiatorPrincipalId: "user_...",
});
req.deletionId; // "del_..."
req.scheduledFor; // ISO 8601; deletion runs after a 7-day grace window
req = await client.request_deletion(
patient_id=patient_id,
reason="patient-initiated",
initiator_principal_id="user_...",
)
The 7-day grace window is load-bearing. It catches accidental deletions, lets clinicians be looped in for clinical-record continuity, and gives the operator console a chance to cancel via the same SDK call (client.cancelDeletion(deletionId)).
What stays, what goes
| Surface | After deletion |
|---|---|
| Reflections | removed |
| Scale captures | removed |
| Evidence maps | removed (all historical projections) |
| Drafts and notes | removed |
| Sandbox forks | removed |
| Audit chain | retained as tombstones; chain hash still verifies |
| Backups (off-site) | overwritten on the next 24-hour rotation after deletion completes |
The audit chain retention is the one thing the SDK cannot opt out of. It is required to prove that the deletion happened, to whom, and when. See HIPAA in-progress for the broader retention posture.
GDPR and CCPA mapping
| Right | SDK call |
|---|---|
| GDPR Art. 15 / CCPA right-to-know | requestExport |
| GDPR Art. 17 / CCPA right-to-delete | requestDeletion |
| GDPR Art. 16 / CCPA right-to-correct | requestDeletion followed by a fresh patient onboarding |
| GDPR Art. 20 right-to-portability | the export bundle's JSON-LD shape is portable as-is |
The SDK does not surface a correction endpoint because the projections are derived from immutable inputs (the reflection, the scale capture). A correction in Nyra's model is a delete plus a re-onboard. The audit chain tombstone makes that operation provable.
Patient vs clinician initiation
A patient-initiated request needs the patient principal. A clinician-initiated request needs data.lifecycle scope plus a written justification that lands in the audit entry. The scope is not granted by default. Agents should not initiate either path; they hand the request to the appropriate principal and surface the deletion ID for tracking.
import { CommitNotAllowedError } from "@humyn/nyra";
// An agent token cannot initiate deletion. This will raise.
try {
await client.requestDeletion({ patientId, reason: "patient-initiated" });
} catch (err) {
if (err instanceof CommitNotAllowedError) {
// Hand the request to the patient's surface, or to a clinician with data.lifecycle scope.
}
}
Next: Versioning and changelog.