PlaySpace

API changelog

Dated changes to the Partner API, the embed tier and @playspace-health/embed, newest first. Anything that can break a working integration is marked Breaking and says what to change.

The Partner API specification is at version 0.1.0, and /v1 in the path is the major version of the surface, not a claim that the contract is frozen. The machine-readable contract is published at /openapi.json, and the endpoint summary is generated from it, so diffing the specification between two dates is always the complete answer.


2026-09-22

Breaking: two embed-token mints are now refused

POST /v1/partner/embed-tokens answers 422 with problem type validation-error, straight away with no warning period, for two requests it used to accept:

  • A patient_id with no capability that acts on a patient. Only form:submit, form:send, storybook:share and worksheet:share act on a named patient. With none of them the binding did nothing but suggest a scoping the token did not have. fields[].path is patient_id. Change: drop patient_id from those mints.
  • games:play in capabilities. A token minted here framed no game. fields[].path is capabilities. Change: mint game seats with POST /v1/partner/game-sessions (createGameSession in the SDK), which issues the clinician and patient seats together.

Partner identifiers: external_id

Clinics, practitioners and patients take an optional external_id, your own identifier for the record, on create and PATCH, and return it on every read. Their lists filter on it with ?external_id=.

  • 1 to 128 printable ASCII characters, exact and case-sensitive, unique per organisation among records that are not deleted. Deleting a record frees its value.
  • On PATCH, a value re-points it, null clears it, and leaving it out leaves it unchanged.
  • A duplicate is 409 with the new problem type external-id-conflict. There is no upsert.
  • A clinician-scoped token can see [] from the lookup and still get the 409, because the colliding record belongs to a clinician it cannot see. That is by design; look it up with your organisation token.
  • Appointments do not take one.

The full rules are in core concepts.

Mint the workspace by area

POST /v1/partner/embed-tokens accepts areas, a list of framed-workspace areas, and area_access, read (the default) or full. Each area expands to its capabilities, shell:read is added, and the result is unioned with capabilities, which is now optional: send at least one of the two. full includes data:export on clients and note:write on clinical-notes. An area never grants a capability bound to one patient. The response's capabilities lists what the token grants. The areas and their sets are on the workspace reference.

Two new frame events

  • token.updated { accepted, reason?, expiresAt }, in every embed mode: the frame's answer to each token pushed into it. accepted: false means it kept its current token, with reason identity_mismatch or unreadable.
  • capability_denied { area, missing }, in the framed workspace: the clinician opened an area the token cannot read (it holds none of the area's read capabilities). Once per area per frame load.

Hosts that ignore unknown events need no change.

Open one client by your patient id

The framed workspace accepts the starting surface clients/by-patient/{partnerPatientId}, where the id is the one POST /v1/partner/patients returned. It opens that client's record, or a "Client not found" panel when the patient is not on this clinician's client list, instead of silently showing the roster. Needs client:read. See the workspace reference.

@playspace-health/embed 0.4.0

  • mintEmbedToken takes areas and areaAccess, and capabilities is optional when areas is sent.
  • Types for token.updated and capability_denied, and the ShellArea / ShellAreaAccess types with SHELL_AREA_KEYS.
  • The package page gains a whole-workspace example with createShellEmbed / <ShellEmbed>, and the no-bundler snippet pins 0.4.0.

Earlier

These did not change a /v1/partner/* schema. They are here because they change a value you get back or what a clinician can do inside a link you minted; read the first even if you skip the rest.

  • Session links are now short redirects. patient_video_url and clinician_video_url are https://<host>/join/<22-character code> — no query string — and opening one redirects to the session credential that used to be in the URL itself. Both fields are still nullable strings, so nothing about the contract moved, and everything the links do when opened is unchanged. If you parse a session link — reading the token out of it, matching on /video-session or /partner-session, or assuming a query string is present — that will stop working: treat the value as an opaque credential and hand it over whole. Two properties to keep in mind: the same code comes back on every call and follows a reschedule, so there is no second link to retire; and the code has no expiry of its own — it stops working once the appointment is cancelled or deleted, or its window has closed. Do not put one through an email provider's click tracking; the tracked rewrite is a plain-http redirect that link scanners pre-fetch, and the pre-fetch follows through to the credential.
  • In-session worksheets gained the desktop editor's controls. A clinician who opens a worksheet from the activity shelf during a session can now add and delete slides and generate AI artwork, and the patient's seat follows those pages live. Pages reach the record only when the clinician files a copy, and page order is fixed for the duration of a session. Nothing about POST /v1/partner/appointments/{id}/session-links changed.

What is safe to build on

The rules that keep an integration working across changes are on the conventions page, and two of them carry the weight:

  • Ignore what you do not recognise in a response. New fields, new enumerated values and new problem types are added within /v1 without a version bump. Preserve unknown fields rather than stripping them, and treat an unrecognised enumerated value as unknown rather than throwing.
  • Branch on a problem type, never on title or detail. The slug set only grows and an existing slug never changes meaning; the prose beside it may be reworded at any time.

How you will hear about a change

Anything that could break you comes from a person as well as this page. A breaking change reaches you through your PlaySpace contact, with the detail and the timing in that conversation. If you need a change window or a stability period written down, raise it and it becomes part of your agreement.

If something has already surprised you, quote the X-Request-Id from the response. It resolves to a single request in our audit history, which is faster than reconstructing what changed.