Guide: Handle a data request

A parent asks for everything you hold about their child. A family moves practices. A client turns eighteen. A clinic leaves your platform.

In each case somebody has to produce the record, and the part that lives in PlaySpace comes out in one call.


The request

const exp = await playspace.exports.create({
  subject: { externalId: 'client_55130' },
  include: [
    'storybooks',
    'worksheets',
    'formResponses',
    'sandtraySaves',
    'dollhouseSaves',
    'whiteboardSnapshots',
    'generatedGames',
    'models',
    'sessionSummaries',
  ],
  format: 'bundle',
  requestedBy: { externalId: 'staff_8842' },
  reason: 'caregiverRequest',
})

const { parts, expiresAt } = await exp.wait({ timeout: '30m' })

Name the families explicitly rather than using '*'. An export is a disclosure. Listing what you are disclosing makes the decision deliberate and leaves a better record of it.

reason is required and constrained: clientRequest, caregiverRequest, clinicalTransfer, migration, legal, internalReview. It is recorded against the export and appears in the audit history.


Who is allowed to ask

requestedBy must resolve to somebody with standing, and PlaySpace checks it.

Requester May export
Clinician Their own clients
Practice administrator Any client in their clinic
Caregiver A dependent whose link carries canRequestExport

A request without standing returns PermissionError with type export-not-permitted.

This is why canRequestExport exists on the caregiver link. Set it when you create the link, at the point where your own system knows who holds consent. Discovering it is unset at the moment a parent asks for records is an avoidable delay.


Clinical notes need three things

Notes are excluded by default and included only when all of the following are true:

  1. The organisation has the notes capability enabled.
  2. notes appears explicitly in include.
  3. The requester is a clinician or a practice administrator.

A caregiver-initiated export never contains notes, regardless of what was requested. That is enforced on our side rather than being left to your interface, because it is the kind of rule that is easy to get wrong once and hard to undo.


What arrives

export_client_55130_2026-09-14/
  manifest.json                  every artifact, attribution, checksum
  README.html                    a human-readable index
  storybooks/
    a-new-school/
      a-new-school.pdf
      pages/ 01.png … 08.png
      storybook.json
  worksheets/
    feelings-animals/
      blank.pdf
      completed-2026-08-19.pdf
      worksheet.json
  forms/
    caregiver-intake-2026-06-02.pdf
    caregiver-intake-2026-06-02.json
  sandtray/
    2026-08-19-session/
      view.png
      scene.glb
      placements.json
  games/
    river-crossing/               unzipped and playable offline
  models/
    terrier.glb
  sessions/
    2026-08-19-summary.pdf

README.html is the part that matters to a parent. They open the folder, double-click one file, and see their child's storybooks and drawings laid out in a page they can read. An export nobody can open is not portability.

manifest.json is the part that matters to a machine. Every artifact with its identifier, type, attribution triple, timestamps, file paths and checksum. Ingesting a PlaySpace export into another system means reading one file.

Every rendered format has a structured twin. No portable-document file carries information its .json omits.


Delivering it

const { parts, expiresAt } = await exp.wait()

for (const part of parts) {
  const bytes = await fetch(part.url).then((r) => r.arrayBuffer())
  await yourSecureDelivery.stage(bytes, { part: part.part })
}

Bundles are available for seven days, then deleted. Request another if you need it again — nothing is lost by letting one expire.

Do not email the link. The download link is credential-bearing: anyone holding it can retrieve the bundle for as long as it is live. Put the bundle behind your own authenticated delivery, the same way you would any other record disclosure.


One artifact instead of the whole record

Often the ask is smaller — a clinician wants the storybook, not the archive.

const { url } = await playspace.artifacts.download('sb_7Hn3xQ', { format: 'pdf' })
Artifact Formats
Storybook pdf, epub, png per page, json
Worksheet, blank or completed pdf, png per page, json
Form response pdf, json, csv
Sandtray or dollhouse save png, glb, json
Whiteboard snapshot png, svg, json
Generated game zip playable offline, json
Three-dimensional model glb, png
Session summary pdf, json
Clinical note pdf, json

A generated game exports as a game that still runs. Unzip it, open the index file, and it plays in a browser with no PlaySpace connection. Content a clinician made should not stop working because a subscription lapsed.


A whole-organisation export

For a platform migration or the end of a contract.

const exp = await playspace.exports.create({
  scope: 'organisation',
  include: ['*'],
  format: 'bundle',
  requestedBy: { externalId: 'staff_0001' },
  reason: 'migration',
})

Structured by clinic, then clinician, then client, with library content that belongs to nobody in a top-level library/ directory.

A large organisation can take hours and run to hundreds of gigabytes, delivered as independently downloadable parts listed in the top-level manifest. Tell partner engineering before your first one so we can watch it.


What is never included

Another person's material. A sandtray two siblings built together exports to both bundles, because both made it. Nothing else crosses.

Uploaded source photographs. An image used to generate a three-dimensional model is discarded after generation. It cannot be exported because it does not exist.

Platform internals. Identifiers, audit rows, tokens and infrastructure metadata are not client records. Engagement telemetry appears in a session summary as durations and catalog titles, not as raw rows.

Anything archived. Archived material is unreadable to every reader on the platform, including an export.


The commitment

There is no export tier, no export fee, and no rate limit designed to make a migration tedious. There is no format here that needs our software to open.

If you are answering a procurement question about vendor lock-in, that is the answer, and it is testable in the sandbox before you sign anything.