Security and compliance
Written for the engineer implementing the integration and for the person who has to answer a security questionnaire about it. Both audiences need the same facts.
The trust boundary
Your backend ──client credentials──▶ PlaySpace Partner API
│ ▲
│ mints │
▼ │
Embed capability token ──▶ Browser ──▶ Cross-origin frame
Client credentials never enter a browser. They authorise everything your organisation can do, and they are long-lived. The browser-safe substitute is an embed capability token: one practitioner, a named list of capabilities, a named list of origins, fifteen minutes by default and an hour at most.
The frame is a real boundary. Nothing inside the surface can read your page, and nothing in your page can read what is inside the surface. That is a property of the browser's origin model rather than a convention either side is asked to respect.
Your authorisation is the boundary we cannot see. PlaySpace verifies that a delegated token names a practitioner in your organisation and that the capabilities and origins you asked for are permissible. It cannot verify that this browser should be that practitioner. The check in your token route is the whole of that decision, and it deserves more tests than anything else you write.
Data we hold, and data we do not
We hold the name and date of birth you send, your identifier for each person, clinics and practitioners, appointments and their sessions, and the content a clinician makes in PlaySpace — storybooks, worksheets, forms and their submissions, saved sandtray and dollhouse scenes, playrooms and toolkits. Where PlaySpace is used for the session itself, the clinical notes written in it, and — when a clinician has PlaySpace draft a note from session audio — the verbatim transcript that draft was made from.
We do not hold your schedule, your billing, your claims, your own clinical notes, your documents, or any part of your record we did not produce. There is no import, no mirror and no nightly file.
Send the minimum. A first initial and a last initial are a complete and acceptable name. Date of birth is optional and is used for age-appropriate content selection; omit it and a clinician sets the age band by hand.
Partner video links are credentials. When you supply appointment video, PlaySpace stores the role-specific pair encrypted and releases only the authenticated participant's URL at join or explicit reload. Provider URLs and encrypted envelopes are excluded from general appointment responses, notifications, logs, analytics, audits, and exports. Disabling URL delivery cannot revoke credentials already received or terminate your provider's call.
Two rules the platform enforces on itself
No name and no free-text search term in a URL. Not a query string, not a path segment, in either direction.
This is not fastidiousness. A platform's own request logs record path and query for every request, at a layer beneath any application-level redaction. The only durable answer is to keep it out of the URL, which makes it a routing decision rather than a scrubbing one.
Identifiers, not values, in every observable channel. Error documents, embed event payloads, audit rows and log lines carry identifiers, statuses, counts and enumerated values. Never a name, a date of birth, a page of story text, a form answer, a note body, or a line of session transcript.
Please hold the same line. A requestId in a support ticket is useful; a client's name in one is a disclosure, and we will ask you to redact it and resend.
Two categories sit just inside the line and are worth naming. Storybook titles are model-generated from a clinician's prompt and can echo details of the child a story was written for. Worksheet titles come from the clinician's own filename or the source document's metadata. Both are fine to render in a clinical interface, and neither belongs in a log, an analytics event or a ticket.
Authentication and authorisation
Client credentials are machine-to-machine credentials issued on approval and exchangeable for an access token that lives 24 hours. Cache and reuse that token rather than minting one per request. The secret is delivered once, by a link that opens once and expires 72 hours after approval.
Scopes are fixed on the credential at approval. A call outside them returns 403. To change what you hold, ask: PlaySpace edits the grant on the credential, and the change takes effect on your next token.
Delegation is the finer grain, and most of the surface requires it. A delegated token is issued acting as one specific practitioner, and the acting practitioner is read from a claim on the token — never from a request body or a query parameter, so a caller cannot mint or read as somebody it was not delegated to. Everything content-shaped works this way: an organisation-wide token has no practitioner to scope to and is refused rather than widened.
Embed capability tokens are minted server-side by exchanging a delegated token. They carry the practitioner, the capability list, the origins, and where a surface writes patient data, the patient. Default lifetime is fifteen minutes and the maximum is one hour, because the token rides in an iframe URL — which is exposed to browser history, Referer headers, and anything that logs a URL. Mint per page load and refresh in place rather than stretching the lifetime.
The subject of a clinical write is set at mint, not in the frame. A form submission's patient is named when the token is minted, checked against both your organisation and the acting clinician's own roster, and cannot be overridden by the browser. Asking for a submit capability without naming a patient is rejected rather than producing a token that would record answers attached to nobody.
Origins are validated at mint, not at frame load. They become the frame's frame-ancestors directive. An origin no browser would honour — a wildcard inside a label, for instance — is a 422 naming the value, which converts the worst failure mode in embedded software, a silently blank rectangle, into an error message on your server.
Revocation is immediate and server-side. Calling logout() on an embed handle revokes every token that embed held; a revoked token is refused by every embed endpoint from the next request on. Unmounting an iframe on its own revokes nothing — the token stays a live bearer credential until it expires. Separately, an embed request re-checks the organisation's standing on every call, so a suspended or revoked organisation's live embeds stop working within the token's lifetime rather than at the end of it.
Isolation
Tenancy is enforced in the database, not in application code. Partner requests run under a dedicated database role whose row-level policies key on the organisation claim in the token, with a second tier that narrows to the delegated practitioner. A missing filter in application code cannot expose another organisation's data, because the policy is beneath it. The explicit filters that do exist in the code are defence in depth, not the boundary.
The privileged database client that bypasses those policies is banned by lint across the entire partner surface, so the ban is mechanical rather than a convention.
404 is deliberately ambiguous. Absent, deleted, belonging to another organisation, and outside the delegated clinician's roster all return the same response. Distinguishing them is exactly the signal an enumeration attack needs.
Deletion is soft. Deleting makes a record unreadable to normal reads; nothing on the partner surface performs a hard delete.
Audit and rate limiting
Every partner request writes one audit row: the operation, the organisation, the acting subject, the resource identifiers touched, the outcome, and a request identifier. Rows are hash-chained, so a row cannot be altered or removed without breaking the chain. Audit rows carry identifiers only — they are readable by people who should not be reading names.
Rate limiting is per organisation across two windows, and the remaining allowance is on every response. Read the headers rather than discovering the limit through 429s.
Mutations that spend money or create a person require an Idempotency-Key, and a repeated key replays the original response rather than acting twice.
The frame
No cookies. Nothing in the embedded surface depends on third-party cookies, so Safari's tracking prevention and Chrome's storage partitioning do not affect it. The capability token lives in the frame's memory for the life of the document, which is why renewal is a callback rather than a refresh cookie.
frame-ancestors is set from the token's origins, so the browser refuses to render the surface anywhere you did not register. Allow the PlaySpace origin you pass as baseUrl in your own Content Security Policy and both directions are explicit — neither side is relying on the other getting it right.
A live session runs one frame deeper, and that frame is protected the same way. When a clinician opens a session inside the framed workspace, PlaySpace frames its own session document inside its own workspace. That inner document is served with a frame-ancestors naming the PlaySpace origin and the host origins from the workspace token, so it can be framed by that workspace and by nothing else — a copy of its address pasted anywhere else renders nothing. The join credential reaches it through same-origin per-tab storage rather than its URL, so the inner address carries nothing secret and neither does your browser history.
Camera and microphone need a matching Permissions-Policy if your page or a proxy in front of it sends one. Without it the browser refuses capture before any permission prompt appears, which looks exactly like a user clicking Block. A session needs more than capture: camera, microphone, display-capture, fullscreen, autoplay and picture-in-picture must all name the PlaySpace origin, and every nesting level has to delegate — the SDK sets the frame's own allow attribute for you, but a header your page sends wins over it.
Events crossing the channel are verified by origin and by source frame before they reach your handler. Anything else is dropped, and unknown event types are dropped rather than thrown on, so a host built against today's contract keeps working against a newer surface.
Generation and third-party models
Storybook generation is the only generation capability on the partner surface. Three things are true of it.
A business associate agreement is in place with every provider in the generation path, covering the specific data flow.
Prompts carry what the clinician wrote. A clinician describing a child's situation to generate a storybook is sending that description to a model provider. That is inherent in the capability, it is covered by agreement, and it is worth your clinicians understanding.
Nothing you send is used to train a model.
There is no metering on generation: no quota endpoint, no per-organisation ceiling and no spend reading. The mandatory idempotency key stops a retry paying twice, and a per-clinic entitlement decides whether a practice can generate at all — there is nothing in between, so a ceiling on your own usage is yours to build. See Content and generation.
Data handling and portability
Portability is the least built part of the platform, and pretending otherwise would be the most expensive thing on this page to get wrong.
There is no partner-facing export. No export endpoint, no bundle format, no manifest describing what a bundle contains, and no authorisation model for a disclosure — nothing that decides who may request a person's records on their behalf, or records that they did. None of it exists, and none of it is close.
What exists instead:
- You read content through the ordinary endpoints. Storybooks, worksheets, playrooms, toolkits and game saves are all readable, and a book or a worksheet can be downloaded as a PDF. If you need a durable copy in your own system, copy it as you go rather than expecting to bulk-extract it later.
- A clinician can export their own PlaySpace data from inside the product. It is a clinician-facing feature in the PlaySpace application, not an API, so it is not something a partner application can trigger or schedule.
- A records request today is handled out of band. Ask partner engineering. There is no self-serve path, and building one to a schedule you have promised a customer is not something a partner integration can do on its own.
Two consequences worth designing around now. First, your record of a PlaySpace id is the join between the two systems — store it when content is created, because there is no cross-type index to reconstruct it from later. Second, signed asset links are credentials with a short life: page images, covers and PDF downloads all come back signed. Deliver them through your own authenticated channel, never in an email, never in a log, and re-read them rather than caching them.
Regional handling
Data residency follows the clinic's country. A clinic in Canada has its data held in Canada; a clinic in the United States, in the United States. Some surfaces are regionally gated and fail closed where availability is not established, which is why country on a practitioner matters — omit it and the surface silently does not appear.
Cross-region access does not happen implicitly. An organisation operating in two regions holds two sets of data.
What we ask of you
- Store client credentials in a secret manager, not in a repository or an environment file that travels with one.
- Never ship a partner secret to a browser. Search your built assets and confirm zero hits before going live.
- Authorise the caller in your token route before minting, and derive the acting practitioner from your own record of who is signed in — never from a client-supplied parameter.
- Rate-limit your token route. From an attacker's point of view it is an unauthenticated-adjacent surface.
- Call
logout()on the embed handle when your user signs out. - Keep client names, storybook titles and worksheet titles out of your logs, your analytics and your support tickets. Send the
requestId.
Questionnaire answers
Short answers to what gets asked most.
Is PlaySpace HIPAA compliant? There is no such thing as HIPAA certification — no accreditation body issues one. The procurement-relevant fact is that PlaySpace executes a business associate agreement covering the data flows described here, and does so with every subprocessor in the generation path.
Where is data held? In the clinic's region. Canada and the United States today.
Is data encrypted? In transit with modern transport security, and at rest.
Do you train models on our data? No.
Can a partner read another partner's data? No, and the enforcement is in the database rather than in application code, so it does not depend on a query being written correctly.
Can one clinician read another clinician's content? No. Content is owned by the practitioner it was created for, and a delegated token narrows to that practitioner in the database as well as in the code.
Is there an audit trail? Yes, one hash-chained row per request, identifiers only.
What happens when we leave? Honestly: there is no export endpoint today, and a records request is handled by partner engineering rather than by an API. If a bulk extraction path is part of your agreement, agree it explicitly rather than assuming this page describes one.
Do you have a penetration test report / audit report? Available under a mutual non-disclosure agreement from partner engineering.
How do we report a vulnerability? security@playspace.health. We acknowledge within one business day and will not pursue a good-faith reporter.