Skip to content
Book a demoSign in
All docs
Agent SDK

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

SurfaceAfter deletion
Reflectionsremoved
Scale capturesremoved
Evidence mapsremoved (all historical projections)
Drafts and notesremoved
Sandbox forksremoved
Audit chainretained 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

RightSDK call
GDPR Art. 15 / CCPA right-to-knowrequestExport
GDPR Art. 17 / CCPA right-to-deleterequestDeletion
GDPR Art. 16 / CCPA right-to-correctrequestDeletion followed by a fresh patient onboarding
GDPR Art. 20 right-to-portabilitythe 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.