Both actions are per adult, authenticated with the adult’s own bearer token, and audited through
AuditService (adult_profile.export, adult_profile.delete). Household membership never
authorizes either action for the other adult.
| Action | Endpoint | SDK operation |
|---|---|---|
| Export | GET /adult-profile/export |
adultProfileExport |
| Deletion | DELETE /adult-profile with header X-Confirm-Deletion: <adultProfileId> |
adultProfileDelete |
format: platepilot.adult-export.v1)adult: public profile ID, label, age-verification timestamp, optional birth month/year.privacy: photo retention preference.household: household ID, the adult’s own sharing settings, and the single fact
sharedWithAnotherAdult. No identifier, label, or record of the other adult is included.subscription: mock billing state (status and period timestamps only; no payment data exists).alertPreferences: reminder opt-ins, when set.diary: every meal (with food items) and water entry the adult owns.planItems: the adult’s own plan items, including confirmed-meal links.photoAnalyses: analysis job metadata (IDs, status, timestamps, retention preference). Photo bytes
and object keys are never exported; the original photo is deleted after analysis unless the adult
opted into 365-day diary retention, and even then only a reference is stored.The export is a plain JSON response to the authenticated adult. There is no archive, link, or retention of the export on the server; nothing is written except the audit event.
202 Accepted with a receipt)X-Confirm-Deletion to equal the caller’s own adultProfileId. A mismatch is a 400
with details.reason = deletion_confirmation_mismatch and deletes nothing.deleteRetainedObject for every
job with a retained object and no retention-deleted marker). Missing objects are ignored. If the
object store is unavailable the request fails with 503 (photo_storage_unavailable) and
nothing is deleted, so the adult can retry.deletion_receipts row holding only a one-way hash of the identity subject.{ receiptId, deletedAt, status: "completed" }. The audit event records the receipt ID as
its resource. Subsequent requests with the same token receive 404 Adult profile not found
until the adult onboards again.Late worker activity cannot recreate deleted data: a claimed job whose rows are gone fails its lease check and the queue message is dropped. Processing-bucket originals that were still in flight fall back to the one-day bucket lifecycle rule.
export PATH=~/.local/share/mise/installs/node/latest/bin:$PATH
A='Authorization: Bearer local.synthetic-token'
curl -s -H "$A" localhost:3001/adult-profile/export | jq '.format, (.diary.meals | length)'
ID=$(curl -s -H "$A" localhost:3001/household | jq -r '.members[] | select(.isMe) | .adultId')
curl -s -i -H "$A" -H "X-Confirm-Deletion: $ID" -X DELETE localhost:3001/adult-profile
Integration coverage: apps/api/src/adult-data/adult-data.repository.test.ts (export scoping,
cascade, household cleanup, receipt), unit coverage in adult-data.service.test.ts.
To remove all intentionally disposable local Docker data instead:
docker compose down -v
This permanently deletes the local PostgreSQL and LocalStack volumes. It is not a substitute for a per-adult deletion.
Response periods, identity-proofing method, legal holds, audit exceptions, and support escalation are PENDING legal/privacy approval.