Portability and exports

A family leaves the practice. A client turns eighteen. A caregiver asks for everything you hold. A clinician moves to another clinic and their client list goes with them. In every one of those cases somebody has to produce the record, and "log into a second system and screenshot it" is not an answer.

Everything PlaySpace holds comes back out, in one call, in formats a person can actually open.


Three levels

One artifact. A single storybook as a portable-document file, a saved sandtray as an image, a form response as structured data.

One person. Everything a client has ever made, packaged.

Everything. Your entire organisation, for a platform migration or a compliance obligation.


One artifact

const pdf = await playspace.artifacts.download('sb_7Hn3xQ', { format: 'pdf' })

Available formats by artifact type:

Artifact Formats
Storybook pdf, epub, png (per page), json
Worksheet, blank or completed pdf, png (per page), json
Form response pdf, json, csv
Sandtray save png (rendered view), glb (scene), json (placements)
Dollhouse save png, glb, json
Whiteboard snapshot png, svg, json
Generated game zip (playable, self-contained), json (manifest)
Three-dimensional model glb, png (preview)
Session summary pdf, json
Clinical note pdf, json

json is always available and always complete. The rendered formats exist so a human can read them; the structured format exists so a machine can. No artifact has a rendered form carrying information its structured form omits.

A generated game exports as a playable bundle. Unzip it, open the index file, and the game a clinician made runs in a browser with no PlaySpace connection. This is deliberate: content generated in a session should not stop working because a subscription lapsed.

Download links are short-lived and single-use. Request one when you are ready to stream it, not in advance.


One person

const exp = await playspace.exports.create({
  subject: { externalId: 'client_55130' },
  include: [
    'storybooks',
    'worksheets',
    'form_responses',
    'sandtray_saves',
    'dollhouse_saves',
    'whiteboard_snapshots',
    'generated_games',
    'models',
    'session_summaries',
    'notes',
  ],
  format: 'bundle',
  requestedBy: { externalId: 'staff_8842' },
  reason: 'client_request',
})

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

include defaults to everything the organisation is licensed for and entitled to read. Naming it explicitly is better practice — an export is a disclosure, and disclosures should be deliberate.

What is in the bundle

export_client_55130_2026-09-14/
  manifest.json                  every artifact, with its attribution and checksum
  README.html                    a human-readable index, openable in a browser
  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
  models/
    terrier.glb
  sessions/
    2026-08-19-summary.pdf
  notes/
    2026-08-19-progress-note.pdf

README.html matters more than it looks. The person receiving this is frequently a parent, not an engineer. They open the folder, double-click one file, and see their child's storybooks and drawings laid out in a page they can read, with everything else linked from it. An export nobody can open is not portability.

manifest.json is the machine's entry point. Every artifact with its identifier, type, attribution triple, creation timestamp, file paths, and a checksum. Ingesting a PlaySpace export into another system means reading one file.

Export status

Exports are jobs. A large caseload with several years of history takes minutes.

exp.status     // 'queued' | 'running' | 'complete' | 'failed' | 'expired'
exp.progress   // 0–100

The finished bundle is available for seven days, then deleted. Request it again if you need it again; nothing is lost by letting one expire.


Everything

For a platform migration, a compliance obligation, or a contract ending.

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

Organisation-scoped exports are structured by clinic, then clinician, then client, with library content that belongs to nobody in a top-level library/ directory. The same manifest and the same formats apply at every level.

A large organisation's full export can take hours and run to hundreds of gigabytes. It is delivered as a set of parts, each independently downloadable, listed in the top-level manifest. Ask partner engineering before running your first one.


Who is allowed to ask

Every export is a disclosure of clinical material, so authorisation is checked and the whole thing is written to an audit log neither of us can edit.

A clinician may export their own clients. A practice administrator may export any client in their clinic. A caregiver may export a dependent for whom canRequestExport is set on the caregiver link — which is why that field exists on caregivers.link rather than being assumed.

requestedBy is required on every export and must resolve to somebody with standing. reason is required and constrained to a fixed set: client_request, caregiver_request, clinical_transfer, migration, legal, internal_review.

Clinical notes are excluded unless three things are true: the organisation has the notes capability enabled, notes is named explicitly in include, and the requester is a clinician or an administrator. A caregiver-initiated export never contains notes, regardless of what was requested.


What is never in an export

Another person's material. A shared session between siblings exports each child's own artifacts to each child's own bundle. A sandtray two people built together exports to both, because both made it.

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

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

Anything soft-deleted. Deleted material is unreadable to everyone, including an export. Deletion means deleted from the perspective of every reader on the platform.


The commitment behind this

We do not make it hard to leave.

There is no format here you need our software to open. There is no artifact that exports as a screenshot of something that was structured. There is no export tier, no export fee, and no rate limit designed to make a migration tedious. A generated game exports as a game that still runs. A storybook exports as a portable-document file a parent can print and a structured file another system can ingest.

If you are evaluating this platform on behalf of a practice that has been burned by a vendor holding its data hostage, this page is the answer to the question you are actually asking.