{
  "openapi": "3.1.0",
  "info": {
    "title": "PlaySpace Partner Platform API",
    "version": "1.0.0",
    "summary": "Bring therapeutic play into your platform.",
    "description": "The PlaySpace Partner Platform API lets a practice-management or electronic\nhealth record platform embed PlaySpace's interactive therapy surfaces —\nsandtray, dollhouse, whiteboard, games — and its generated content —\nstorybooks, worksheets, forms, three-dimensional models — into its own\nproduct.\n\n## Authentication\n\nExchange your client credentials for a bearer token at `POST /oauth/token`,\nthen send it as `Authorization: Bearer <token>` on every request. Access\ntokens live one hour. Client credentials are server credentials and must\nnever reach a browser: to render a surface in a browser, mint a short-lived\nsession token at `POST /tokens` and hand that to the front end instead.\n\n## Identity is a pointer, not a copy\n\nYour platform is the record of truth for people. Every clinic, practitioner\nand patient carries an `external_id` — your identifier — and every path that\naccepts a PlaySpace identifier also accepts yours, prefixed `ext:`.\n\n```\nGET /v1/patients/pt_9Kd2mXwF\nGET /v1/patients/ext:client_55130\n```\n\nWrite verbs on identity resources are upserts keyed on `external_id`. They\ncreate on first sight and update thereafter, so they are safe to call on\nevery synchronisation pass and there is nothing to reconcile.\n\n## Envelopes\n\nEvery success response is `{ \"data\": ..., \"meta\": ... }`. `meta` always\ncarries `request_id` and `generated_at`, and carries `pagination` on list\nresponses. Every failure is an RFC 9457 problem document served as\n`application/problem+json`.\n\n## Idempotency\n\n`POST`, `PATCH` and `DELETE` require an `Idempotency-Key` header. A repeat\nwith the same key and the same body replays the original response byte for\nbyte. The same key with a different body is rejected with\n`422 idempotency-key-mismatch`. Keys are retained 24 hours.\n\n## Pagination\n\nCursor-based. Pass `limit` up to 100 and follow `meta.pagination.next_cursor`.\nOffsets are not supported.\n\n## Rate limits\n\nTwo windows per organisation, minute and hour, with the more restrictive one\nwinning. Every response carries the remaining count and reset time for both.\n\n## Protected health information\n\nNo person's name and no free-text search term ever travels in a URL, in\neither direction. Person search is `POST /patients/search` with a request\nbody, and there is no `GET` equivalent. Change-feed payloads and error\ndocuments carry identifiers, statuses, counts and timestamps only.\n\n## Deletion\n\nDeletion is soft and unenumerable. A `DELETE` archives a record rather than\ndestroying it, and reading something absent, archived, or belonging to\nanother organisation all return the same `404`.\n",
    "termsOfService": "https://playspace.health/partner-terms",
    "contact": {
      "name": "PlaySpace Partner Engineering",
      "url": "https://developers.playspace.health/support",
      "email": "partner-engineering@playspace.health"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://playspace.health/partner-terms"
    }
  },
  "externalDocs": {
    "description": "Developer portal",
    "url": "https://developers.playspace.health"
  },
  "servers": [
    {
      "url": "https://api.playspace.health/v1",
      "description": "Production"
    },
    {
      "url": "https://api.sandbox.playspace.health/v1",
      "description": "Sandbox — synthetic data, weekly reset, no outbound messages"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Organisation",
      "description": "Your organisation, the surfaces it is licensed for, its practice hierarchy."
    },
    {
      "name": "Identity",
      "description": "Clinics, practitioners, patients and caregiver relationships. Write verbs are\nupserts keyed on `external_id`.\n"
    },
    {
      "name": "Sessions",
      "description": "A session is one appointment's worth of PlaySpace. Everything produced inside\none is attributed to it.\n"
    },
    {
      "name": "Tokens",
      "description": "Short-lived, role-scoped, single-person tokens that authorise a browser\nsurface. The token carries the role, so components need no role parameter.\n"
    },
    {
      "name": "Content",
      "description": "Playrooms, toolkits, games, storybooks, worksheets and forms."
    },
    {
      "name": "Generation",
      "description": "Creating new content. Every operation here is asynchronous, returns a job, and\nis metered against the organisation's quota.\n"
    },
    {
      "name": "Jobs",
      "description": "Status of asynchronous work."
    },
    {
      "name": "Artifacts",
      "description": "The single index over everything PlaySpace produced and kept, attributed to a\nclinician, optionally a client, and optionally a session.\n"
    },
    {
      "name": "Exports",
      "description": "Packaging a person's or an organisation's record for portability."
    },
    {
      "name": "Notes",
      "description": "Clinical documentation, read-only, available only to organisations with the\nnotes capability enabled.\n"
    },
    {
      "name": "Changes",
      "description": "One ordered, resumable feed of everything that changed."
    },
    {
      "name": "Usage",
      "description": "Generation quota consumption."
    }
  ],
  "paths": {
    "/oauth/token": {
      "post": {
        "operationId": "createAccessToken",
        "summary": "Exchange client credentials for an access token",
        "tags": [
          "Organisation"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "client_secret",
                  "grant_type"
                ],
                "properties": {
                  "client_id": {
                    "type": "string",
                    "examples": [
                      "psc_live_4f81c2a9"
                    ]
                  },
                  "client_secret": {
                    "type": "string",
                    "format": "password"
                  },
                  "grant_type": {
                    "type": "string",
                    "const": "client_credentials"
                  },
                  "scope": {
                    "type": "string",
                    "description": "Space-delimited. Omit to receive every scope granted to your organisation.",
                    "examples": [
                      "identity:write sessions:write content:read generation:write"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "An access token.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "access_token",
                    "token_type",
                    "expires_in"
                  ],
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "token_type": {
                      "type": "string",
                      "const": "Bearer"
                    },
                    "expires_in": {
                      "type": "integer",
                      "examples": [
                        3600
                      ]
                    },
                    "scope": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/organisation": {
      "get": {
        "operationId": "getOrganisation",
        "summary": "Retrieve your organisation",
        "tags": [
          "Organisation"
        ],
        "responses": {
          "200": {
            "description": "Your organisation.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Organisation"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/organisation/surfaces": {
      "get": {
        "operationId": "listLicensedSurfaces",
        "summary": "List the surfaces this organisation is licensed for",
        "description": "Render your own entry points from this list. A surface absent here will be\nrefused at token mint with `entitlement-denied`, so filtering ahead of time\nkeeps a clinician from clicking something that cannot work.\n",
        "tags": [
          "Organisation"
        ],
        "responses": {
          "200": {
            "description": "Licensed surfaces.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "surface": {
                                "$ref": "#/components/schemas/Surface"
                              },
                              "licensed": {
                                "type": "boolean"
                              },
                              "embeddable": {
                                "type": "boolean",
                                "description": "Whether this surface can render in a partner-hosted frame."
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/organisation/groups": {
      "get": {
        "operationId": "listPracticeGroups",
        "summary": "List practice groups",
        "description": "The optional hierarchy level above the clinic, for multi-location practices.",
        "tags": [
          "Organisation"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Practice groups.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/PracticeGroup"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/clinics": {
      "get": {
        "operationId": "listClinics",
        "summary": "List clinics",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "group",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Restrict to one practice group."
          },
          {
            "$ref": "#/components/parameters/includeArchived"
          }
        ],
        "responses": {
          "200": {
            "description": "Clinics.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Clinic"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "upsertClinic",
        "summary": "Create or update a clinic",
        "description": "Keyed on `external_id`. Creates on first sight, updates thereafter.",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClinicWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The clinic was updated.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Clinic"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "201": {
            "description": "The clinic was created.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Clinic"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/clinics/{clinicId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/clinicId"
        }
      ],
      "get": {
        "operationId": "getClinic",
        "summary": "Retrieve a clinic",
        "tags": [
          "Identity"
        ],
        "responses": {
          "200": {
            "description": "The clinic.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Clinic"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateClinic",
        "summary": "Update a clinic",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClinicWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated clinic.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Clinic"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "delete": {
        "operationId": "archiveClinic",
        "summary": "Archive a clinic",
        "description": "Soft. The clinic becomes unreadable rather than being destroyed. A clinic with\nactive practitioners or patients is refused with `clinic-not-empty`.\n",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Archived."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/practitioners": {
      "get": {
        "operationId": "listPractitioners",
        "summary": "List practitioners",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "clinic",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/includeArchived"
          }
        ],
        "responses": {
          "200": {
            "description": "Practitioners.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Practitioner"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "upsertPractitioner",
        "summary": "Create or update a practitioner",
        "description": "Keyed on `external_id`. A practitioner your platform vouches for is a\npractitioner PlaySpace accepts — no PlaySpace account, invitation or login is\ncreated, and identity flows one way, from you to us.\n",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PractitionerWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Practitioner"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "201": {
            "description": "Created.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Practitioner"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/practitioners/{practitionerId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/practitionerId"
        }
      ],
      "get": {
        "operationId": "getPractitioner",
        "summary": "Retrieve a practitioner",
        "tags": [
          "Identity"
        ],
        "responses": {
          "200": {
            "description": "The practitioner.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Practitioner"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updatePractitioner",
        "summary": "Update a practitioner",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PractitionerWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated practitioner.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Practitioner"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "delete": {
        "operationId": "archivePractitioner",
        "summary": "Archive a practitioner",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Archived."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/patients": {
      "get": {
        "operationId": "listPatients",
        "summary": "List patients",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "clinic",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "practitioner",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Restrict to one clinician's caseload."
          },
          {
            "$ref": "#/components/parameters/includeArchived"
          }
        ],
        "responses": {
          "200": {
            "description": "Patients.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Patient"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "upsertPatient",
        "summary": "Create or update a patient",
        "description": "Keyed on `external_id`. Send the minimum that lets a clinician recognise the\nright person in a list — a first initial and a last initial are a complete and\nacceptable name.\n",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatientWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Patient"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "201": {
            "description": "Created.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Patient"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/patients/search": {
      "post": {
        "operationId": "searchPatients",
        "summary": "Search patients",
        "description": "A `POST` deliberately, and there is no `GET` equivalent. A person's name or a\nfree-text term in a query string is captured by platform request logs before\nanything at the application layer can act on it, so the term travels in a body\ninstead. This is a routing decision, not a redaction one.\n",
        "tags": [
          "Identity"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query"
                ],
                "properties": {
                  "query": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120
                  },
                  "clinic": {
                    "type": "string"
                  },
                  "practitioner": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100,
                    "default": 25
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Matches, ranked by relevance.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Patient"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/patients/{patientId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/patientId"
        }
      ],
      "get": {
        "operationId": "getPatient",
        "summary": "Retrieve a patient",
        "tags": [
          "Identity"
        ],
        "responses": {
          "200": {
            "description": "The patient.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Patient"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updatePatient",
        "summary": "Update a patient",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PatientWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated patient.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Patient"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "delete": {
        "operationId": "archivePatient",
        "summary": "Archive a patient",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Archived."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/patients/{patientId}/caregivers": {
      "parameters": [
        {
          "$ref": "#/components/parameters/patientId"
        }
      ],
      "get": {
        "operationId": "listPatientCaregivers",
        "summary": "List a patient's caregivers",
        "tags": [
          "Identity"
        ],
        "responses": {
          "200": {
            "description": "Caregiver links.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/CaregiverLink"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/caregivers/link": {
      "post": {
        "operationId": "linkCaregiver",
        "summary": "Link a dependent to a caregiver",
        "description": "The relationship that decides who receives a session link, who may join\nalongside a child, and who may request an export. In a paediatric caseload it\nis load-bearing rather than decorative.\n",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "patient",
                  "caregiver",
                  "relationship"
                ],
                "properties": {
                  "patient": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "caregiver": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "relationship": {
                    "$ref": "#/components/schemas/CaregiverRelationship"
                  },
                  "receives_session_links": {
                    "type": "boolean",
                    "default": true
                  },
                  "can_request_export": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Linked.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/CaregiverLink"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/caregivers/link/{linkId}": {
      "parameters": [
        {
          "name": "linkId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "operationId": "unlinkCaregiver",
        "summary": "Remove a caregiver link",
        "tags": [
          "Identity"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Unlinked."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/sessions": {
      "get": {
        "operationId": "listSessions",
        "summary": "List sessions",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/SessionStatus"
            }
          },
          {
            "name": "clinician",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "patient",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "scheduled_after",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "scheduled_before",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sessions.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Session"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createSession",
        "summary": "Create a session",
        "description": "Created against an appointment that lives in your scheduler. PlaySpace does not\nneed the appointment itself, only your identifier for it, so that everything the\nsession produces can be traced back to it.\n\n`surfaces` is the ceiling for this session. A surface absent from it cannot be\nopened by anyone in the session, whatever a token or a front end asks for.\n",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SessionCreate"
              },
              "examples": {
                "virtual": {
                  "summary": "A virtual appointment with one child",
                  "value": {
                    "appointment": {
                      "external_id": "appt_11923",
                      "scheduled_at": "2026-09-02T15:00:00Z"
                    },
                    "clinician": {
                      "external_id": "staff_8842"
                    },
                    "participants": [
                      {
                        "patient": {
                          "external_id": "client_55130"
                        }
                      }
                    ],
                    "playroom": "child-default",
                    "surfaces": [
                      "sandtray",
                      "dollhouse",
                      "whiteboard",
                      "games"
                    ],
                    "notify": {
                      "patient": true,
                      "clinician": false
                    }
                  }
                },
                "homework": {
                  "summary": "An asynchronous container for work done between appointments",
                  "value": {
                    "appointment": {
                      "external_id": "homework_2026_w36"
                    },
                    "clinician": {
                      "external_id": "staff_8842"
                    },
                    "participants": [
                      {
                        "patient": {
                          "external_id": "client_55130"
                        }
                      }
                    ],
                    "surfaces": [
                      "worksheets",
                      "storybooks"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The session, with both role-scoped hosted links.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Session"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/sessions/{sessionId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/sessionId"
        }
      ],
      "get": {
        "operationId": "getSession",
        "summary": "Retrieve a session",
        "tags": [
          "Sessions"
        ],
        "responses": {
          "200": {
            "description": "The session. Hosted links are omitted; re-mint them explicitly.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Session"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateSession",
        "summary": "Update a session",
        "description": "Reschedule, change the playroom, or narrow the permitted surfaces.",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scheduled_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "playroom": {
                    "type": "string"
                  },
                  "surfaces": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Surface"
                    }
                  },
                  "notify": {
                    "$ref": "#/components/schemas/NotifyFlags"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated session.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Session"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/sessions/{sessionId}/end": {
      "parameters": [
        {
          "$ref": "#/components/parameters/sessionId"
        }
      ],
      "post": {
        "operationId": "endSession",
        "summary": "End a session",
        "description": "Moves the session to `complete` and finalises its summary.",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The completed session.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Session"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/sessions/{sessionId}/cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/sessionId"
        }
      ],
      "post": {
        "operationId": "cancelSession",
        "summary": "Cancel a session",
        "description": "Revokes every outstanding token and link for the session.",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "notify": {
                    "$ref": "#/components/schemas/NotifyFlags"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The cancelled session.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Session"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/sessions/{sessionId}/summary": {
      "parameters": [
        {
          "$ref": "#/components/parameters/sessionId"
        }
      ],
      "get": {
        "operationId": "getSessionSummary",
        "summary": "Retrieve what happened in a session",
        "tags": [
          "Sessions"
        ],
        "responses": {
          "200": {
            "description": "Duration, participants, surfaces used, artifacts produced and games played.\n\nThree caveats before building a report from `games_played`. Only the\nclinician's play bracket is persisted, because a participant on a session\ntoken holds no account identity. Every duration is an upper bound, since a\nbackgrounded tab keeps accruing. And a remount can split one continuous play\ninto two entries, so sum by game rather than assuming one entry per play.\n",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/SessionSummary"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/sessions/{sessionId}/links": {
      "parameters": [
        {
          "$ref": "#/components/parameters/sessionId"
        }
      ],
      "post": {
        "operationId": "createSessionLinks",
        "summary": "Re-mint both hosted session links",
        "description": "Hosted links are ephemeral and are never stored. Mint them when you are about to\nput them in front of somebody.\n",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "Freshly minted links.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/SessionLinks"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "The session is not link-eligible — for example an in-person session with no\nvirtual room.\n",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/tokens": {
      "post": {
        "operationId": "issueSessionToken",
        "summary": "Issue a session token for one person",
        "description": "The token carries the role, so a browser component needs no role parameter and\ncannot be handed the wrong one.\n\nA `clinician` token renders the facilitation side of every surface — the figure\nlibrary, tool palette, navigation, saves and session controls. A `patient` token\nrenders the participation side only.\n\nThree gates apply at mint, and all three fail loudly rather than issuing a token\nthat will not work: the requested surfaces must be within the session's ceiling,\nevery origin must be one a browser will honour as a frame source, and the\norganisation must be licensed for each surface.\n",
        "tags": [
          "Tokens"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenCreate"
              },
              "examples": {
                "clinician": {
                  "summary": "The clinician's token",
                  "value": {
                    "session": "sess_2Nk8pQvR7xLm",
                    "subject": {
                      "external_id": "staff_8842"
                    },
                    "role": "clinician",
                    "origins": [
                      "https://app.example-practice.com"
                    ],
                    "ttl": "15m"
                  }
                },
                "patient": {
                  "summary": "The child's token, narrowed to one surface",
                  "value": {
                    "session": "sess_2Nk8pQvR7xLm",
                    "subject": {
                      "external_id": "client_55130"
                    },
                    "role": "patient",
                    "surfaces": [
                      "sandtray"
                    ],
                    "origins": [
                      "https://app.example-practice.com"
                    ],
                    "ttl": "60m"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The token.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/SessionToken"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The organisation is not licensed for a requested surface\n(`entitlement-denied`).\n",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "A requested surface exceeds the session's ceiling (`capability-missing`), or\nan origin is not one a browser will honour (`origin-not-allowed`).\n",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/tokens/{tokenId}": {
      "parameters": [
        {
          "name": "tokenId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "operationId": "revokeSessionToken",
        "summary": "Revoke a session token immediately",
        "description": "Useful when a clinician signs out of your application mid-session.",
        "tags": [
          "Tokens"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/playrooms": {
      "get": {
        "operationId": "listPlayrooms",
        "summary": "List playrooms",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PlayroomType"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Playrooms.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Playroom"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createPlayroom",
        "summary": "Create a playroom",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlayroomWrite"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Playroom"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/playrooms/{playroomId}": {
      "parameters": [
        {
          "name": "playroomId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getPlayroom",
        "summary": "Retrieve a playroom",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "The playroom, with its ordered contents.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Playroom"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updatePlayroom",
        "summary": "Update a playroom",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlayroomWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated playroom.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Playroom"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "archivePlayroom",
        "summary": "Archive a playroom",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Archived."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/playrooms/{playroomId}/contents": {
      "parameters": [
        {
          "name": "playroomId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "attachPlayroomContent",
        "summary": "Attach content to a playroom",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContentRef"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Attached.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/PlayroomContent"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/playrooms/{playroomId}/contents/{contentId}": {
      "parameters": [
        {
          "name": "playroomId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "contentId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "operationId": "detachPlayroomContent",
        "summary": "Detach content from a playroom",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Detached."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/toolkits": {
      "get": {
        "operationId": "listToolkits",
        "summary": "List toolkits",
        "description": "A toolkit is a lighter-weight curated set of the same content a playroom holds.",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Toolkits.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Toolkit"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createToolkit",
        "summary": "Create a toolkit",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ToolkitWrite"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Toolkit"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/toolkits/{toolkitId}": {
      "parameters": [
        {
          "name": "toolkitId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getToolkit",
        "summary": "Retrieve a toolkit",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "The toolkit.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Toolkit"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateToolkit",
        "summary": "Update a toolkit",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ToolkitWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated toolkit.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Toolkit"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "archiveToolkit",
        "summary": "Archive a toolkit",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Archived."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/games": {
      "get": {
        "operationId": "listGames",
        "summary": "List the game catalog",
        "description": "The catalog this organisation is licensed for, across every provider. Filter by\ntherapeutic skill, player count, age range or category.\n",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "skills",
            "in": "query",
            "description": "Comma-delimited. Matches a game developing any of them.",
            "schema": {
              "type": "string",
              "examples": [
                "emotional-regulation,turn-taking"
              ]
            }
          },
          {
            "name": "players",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "age_min",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "age_max",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "provider",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/GameProvider"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Games.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Game"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/games/{gameSlug}": {
      "parameters": [
        {
          "name": "gameSlug",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getGame",
        "summary": "Retrieve one game",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "The game.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Game"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/storybooks": {
      "get": {
        "operationId": "listStorybooks",
        "summary": "List storybooks",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/filterSubject"
          },
          {
            "$ref": "#/components/parameters/filterCreatedBy"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/ContentStatus"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Storybooks.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Storybook"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/storybooks/generate": {
      "post": {
        "operationId": "generateStorybook",
        "summary": "Generate a storybook",
        "description": "Asynchronous. Returns a job; poll it or watch the change feed for\n`artifact.ready`. Eight pages typically takes two to four minutes.\n\nNaming characters up front produces a visual description carried into every\npage's image generation, so a character looks the same on page eight as on page\none. Omit `characters` and they are inferred from the prompt and locked after the\nfirst page.\n",
        "tags": [
          "Generation"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StorybookGenerate"
              },
              "examples": {
                "personal": {
                  "summary": "A story for one child",
                  "value": {
                    "clinician": {
                      "external_id": "staff_8842"
                    },
                    "subject": {
                      "external_id": "client_55130"
                    },
                    "session": {
                      "external_id": "appt_11923"
                    },
                    "prompt": "A story about starting at a new school, for a child who is worried nobody will sit with them at lunch.",
                    "pages": 8,
                    "age_group": "5-8",
                    "style": "watercolour",
                    "reading_level": "grade-2",
                    "characters": [
                      {
                        "name": "Nia",
                        "role": "protagonist",
                        "description": "seven years old, box braids, yellow raincoat"
                      },
                      {
                        "name": "Pip",
                        "role": "supporting",
                        "description": "a small grey cat with one white ear"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. A job is running.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Job"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/storybooks/{storybookId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/storybookId"
        }
      ],
      "get": {
        "operationId": "getStorybook",
        "summary": "Retrieve a storybook",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "The storybook, with its ordered pages.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Storybook"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateStorybook",
        "summary": "Update a storybook",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "published": {
                    "$ref": "#/components/schemas/PublishTarget"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated storybook.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Storybook"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "archiveStorybook",
        "summary": "Archive a storybook",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Archived."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/storybooks/{storybookId}/pages": {
      "parameters": [
        {
          "$ref": "#/components/parameters/storybookId"
        }
      ],
      "get": {
        "operationId": "listStorybookPages",
        "summary": "List a storybook's pages",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "Pages, in reading order.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/StorybookPage"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/storybooks/pages/{pageId}": {
      "parameters": [
        {
          "name": "pageId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "operationId": "updateStorybookPage",
        "summary": "Edit a page's text",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated page.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/StorybookPage"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/storybooks/pages/{pageId}/regenerate": {
      "parameters": [
        {
          "name": "pageId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "regenerateStorybookPage",
        "summary": "Regenerate one page's illustration",
        "description": "Leaves every other page untouched and preserves character consistency.",
        "tags": [
          "Generation"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "prompt": {
                    "type": "string",
                    "description": "An additional instruction. Omit to regenerate from the existing prompt."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Job"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/storybooks/{storybookId}/assign": {
      "parameters": [
        {
          "$ref": "#/components/parameters/storybookId"
        }
      ],
      "post": {
        "operationId": "assignStorybook",
        "summary": "Assign a storybook to a client",
        "description": "Creates a personal copy that tracks reading position, so a child can read it\nbetween appointments and the clinician can see how far they got.\n",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subject"
                ],
                "properties": {
                  "subject": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "notify_caregiver": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The assigned copy.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Artifact"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/worksheets": {
      "get": {
        "operationId": "listWorksheets",
        "summary": "List worksheets",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/filterSubject"
          },
          {
            "$ref": "#/components/parameters/filterCreatedBy"
          }
        ],
        "responses": {
          "200": {
            "description": "Worksheets.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Worksheet"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createWorksheet",
        "summary": "Create a worksheet",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorksheetWrite"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Worksheet"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/worksheets/generate": {
      "post": {
        "operationId": "generateWorksheet",
        "summary": "Generate a worksheet",
        "tags": [
          "Generation"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorksheetGenerate"
              },
              "examples": {
                "default": {
                  "value": {
                    "clinician": {
                      "external_id": "staff_8842"
                    },
                    "subject": {
                      "external_id": "client_55130"
                    },
                    "prompt": "A feelings-identification worksheet using animals rather than faces.",
                    "pages": 3,
                    "age_group": "5-8",
                    "include_imagery": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Job"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/worksheets/{worksheetId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/worksheetId"
        }
      ],
      "get": {
        "operationId": "getWorksheet",
        "summary": "Retrieve a worksheet",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "The worksheet, with its pages.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Worksheet"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateWorksheet",
        "summary": "Update a worksheet",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorksheetWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated worksheet.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Worksheet"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "archiveWorksheet",
        "summary": "Archive a worksheet",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Archived."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/worksheets/{worksheetId}/assign": {
      "parameters": [
        {
          "$ref": "#/components/parameters/worksheetId"
        }
      ],
      "post": {
        "operationId": "assignWorksheet",
        "summary": "Assign a worksheet as homework",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subject"
                ],
                "properties": {
                  "subject": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "due_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "notify_caregiver": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The assigned copy.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Artifact"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/worksheets/copies": {
      "get": {
        "operationId": "listWorksheetCopies",
        "summary": "List completed worksheet copies",
        "description": "A completed copy is its own artifact, distinct from the blank worksheet it came\nfrom. The blank is library content with no subject; the copy has one.\n",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/filterSubject"
          },
          {
            "name": "worksheet",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "assigned",
                "in_progress",
                "complete"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Copies.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/WorksheetCopy"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/forms": {
      "get": {
        "operationId": "listForms",
        "summary": "List forms",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/filterCreatedBy"
          }
        ],
        "responses": {
          "200": {
            "description": "Forms.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Form"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createForm",
        "summary": "Create a form",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FormWrite"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Form"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/forms/generate": {
      "post": {
        "operationId": "generateForm",
        "summary": "Generate a form from a prompt",
        "tags": [
          "Generation"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "clinician",
                  "prompt"
                ],
                "properties": {
                  "clinician": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "subject": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "session": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "prompt": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 2000
                  },
                  "max_fields": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 60,
                    "default": 20
                  },
                  "scored": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. Typically under thirty seconds.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Job"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/forms/extract": {
      "post": {
        "operationId": "extractForm",
        "summary": "Build a form from an uploaded document",
        "description": "Reads an existing portable-document or image intake sheet and produces a\nstructured, fillable form from it. The single most common request from clinics\nwith twenty years of paper.\n",
        "tags": [
          "Generation"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "clinician",
                  "file"
                ],
                "properties": {
                  "clinician": {
                    "type": "string",
                    "description": "A PlaySpace or `ext:`-prefixed practitioner identifier."
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Portable document, PNG or JPEG. Maximum 20 megabytes."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Job"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/forms/{formId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/formId"
        }
      ],
      "get": {
        "operationId": "getForm",
        "summary": "Retrieve a form",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "The form, with its field definitions.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Form"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "operationId": "updateForm",
        "summary": "Update a form",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FormWrite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated form.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Form"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "operationId": "archiveForm",
        "summary": "Archive a form",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Archived."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/forms/{formId}/assign": {
      "parameters": [
        {
          "$ref": "#/components/parameters/formId"
        }
      ],
      "post": {
        "operationId": "assignForm",
        "summary": "Assign a form for completion",
        "description": "Set `route_to_caregiver` for a paediatric client so the link reaches the adult\nwho holds consent rather than the child.\n",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subject"
                ],
                "properties": {
                  "subject": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "route_to_caregiver": {
                    "type": "boolean",
                    "default": false
                  },
                  "due_at": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The assignment.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Artifact"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/forms/responses": {
      "get": {
        "operationId": "listFormResponses",
        "summary": "List form responses",
        "description": "Structured answers, never a rendered blob.",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/filterSubject"
          },
          {
            "name": "form",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "submitted_after",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Responses.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/FormResponse"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/forms/responses/{responseId}": {
      "parameters": [
        {
          "name": "responseId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getFormResponse",
        "summary": "Retrieve one form response",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "The response.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/FormResponse"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/studio/generate": {
      "post": {
        "operationId": "generateGame",
        "summary": "Generate a playable game",
        "description": "A clinician describes a game and gets a real playable application, launchable in\na session like any catalog game. Four to eight minutes.\n",
        "tags": [
          "Generation"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "clinician",
                  "prompt"
                ],
                "properties": {
                  "clinician": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "subject": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "session": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "prompt": {
                    "type": "string",
                    "minLength": 20,
                    "maxLength": 2000
                  },
                  "players": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 4,
                    "default": 2
                  },
                  "duration": {
                    "type": "string",
                    "enum": [
                      "short",
                      "medium",
                      "long"
                    ],
                    "default": "short"
                  }
                }
              },
              "examples": {
                "default": {
                  "value": {
                    "clinician": {
                      "external_id": "staff_8842"
                    },
                    "subject": {
                      "external_id": "client_55130"
                    },
                    "prompt": "A two-player cooperative game where you take turns naming a feeling to move a boat across a river.",
                    "players": 2,
                    "duration": "short"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Job"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/studio/games": {
      "get": {
        "operationId": "listGeneratedGames",
        "summary": "List generated games",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/filterSubject"
          },
          {
            "$ref": "#/components/parameters/filterCreatedBy"
          }
        ],
        "responses": {
          "200": {
            "description": "Generated games.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/GeneratedGame"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/models/generate": {
      "post": {
        "operationId": "generateModel",
        "summary": "Generate a three-dimensional model",
        "description": "A figure that is not in the sandtray library, from a description or a photograph,\nusable in the tray immediately. Three to six minutes.\n\nAn uploaded photograph is used to produce the model and then discarded. It is not\nretained, not used for training, and never appears in an export. The capability\nexists for objects, pets and places — do not upload an image containing a person.\n",
        "tags": [
          "Generation"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "clinician",
                  "source",
                  "prompt"
                ],
                "properties": {
                  "clinician": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "session": {
                    "$ref": "#/components/schemas/Ref"
                  },
                  "source": {
                    "type": "string",
                    "const": "text"
                  },
                  "prompt": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 500
                  }
                }
              },
              "examples": {
                "text": {
                  "value": {
                    "clinician": {
                      "external_id": "staff_8842"
                    },
                    "source": "text",
                    "prompt": "a small brown terrier with a red collar, sitting"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "clinician",
                  "source",
                  "file"
                ],
                "properties": {
                  "clinician": {
                    "type": "string"
                  },
                  "source": {
                    "type": "string",
                    "const": "image"
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "PNG or JPEG. Maximum 10 megabytes. Discarded after generation."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Job"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/models": {
      "get": {
        "operationId": "listModels",
        "summary": "List generated three-dimensional models",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/filterCreatedBy"
          }
        ],
        "responses": {
          "200": {
            "description": "Models.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Model"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/jobs": {
      "get": {
        "operationId": "listJobs",
        "summary": "List jobs",
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/JobStatus"
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/JobType"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Jobs.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Job"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/jobs/{jobId}": {
      "parameters": [
        {
          "name": "jobId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getJob",
        "summary": "Retrieve a job",
        "description": "A failed job carries a `reason` written for a clinician rather than an engineer,\nand does not consume quota.\n",
        "tags": [
          "Jobs"
        ],
        "responses": {
          "200": {
            "description": "The job, with its result when complete.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Job"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/jobs/{jobId}/cancel": {
      "parameters": [
        {
          "name": "jobId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "operationId": "cancelJob",
        "summary": "Cancel a job",
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The cancelled job.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Job"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "The job already reached a terminal state.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/artifacts": {
      "get": {
        "operationId": "listArtifacts",
        "summary": "List artifacts",
        "description": "The single index over everything PlaySpace produced and kept. Queryable from any\nend of the attribution triple.\n",
        "tags": [
          "Artifacts"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/filterSubject"
          },
          {
            "$ref": "#/components/parameters/filterCreatedBy"
          },
          {
            "name": "session",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/ArtifactType"
            }
          },
          {
            "name": "created_after",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Artifacts.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Artifact"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/artifacts/{artifactId}": {
      "parameters": [
        {
          "name": "artifactId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getArtifact",
        "summary": "Retrieve an artifact",
        "tags": [
          "Artifacts"
        ],
        "responses": {
          "200": {
            "description": "The artifact.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Artifact"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/artifacts/{artifactId}/download": {
      "parameters": [
        {
          "name": "artifactId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "format",
          "in": "query",
          "required": true,
          "schema": {
            "$ref": "#/components/schemas/DownloadFormat"
          }
        }
      ],
      "get": {
        "operationId": "downloadArtifact",
        "summary": "Download an artifact",
        "description": "Returns a short-lived, single-use link. Request one when you are ready to stream\nit, not in advance.\n\n`json` is available for every artifact type and is always complete: no rendered\nformat carries information its structured form omits.\n",
        "tags": [
          "Artifacts"
        ],
        "responses": {
          "200": {
            "description": "A download link.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "string",
                              "format": "uri"
                            },
                            "format": {
                              "$ref": "#/components/schemas/DownloadFormat"
                            },
                            "size_bytes": {
                              "type": "integer"
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "That format is not available for this artifact type.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/exports": {
      "get": {
        "operationId": "listExports",
        "summary": "List exports",
        "description": "Every export is a disclosure and is retained in the audit history.",
        "tags": [
          "Exports"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/filterSubject"
          }
        ],
        "responses": {
          "200": {
            "description": "Exports.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Export"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createExport",
        "summary": "Create an export",
        "description": "`requested_by` must resolve to somebody with standing: a clinician for their own\nclients, an administrator for any client in their clinic, or a caregiver for a\ndependent whose link carries `can_request_export`.\n\nClinical notes are included only when the organisation has the notes capability,\n`notes` is named explicitly in `include`, and the requester is a clinician or an\nadministrator. A caregiver-initiated export never contains notes.\n",
        "tags": [
          "Exports"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExportCreate"
              },
              "examples": {
                "person": {
                  "summary": "Everything one client has made",
                  "value": {
                    "subject": {
                      "external_id": "client_55130"
                    },
                    "include": [
                      "storybooks",
                      "worksheets",
                      "form_responses",
                      "sandtray_saves",
                      "session_summaries"
                    ],
                    "format": "bundle",
                    "requested_by": {
                      "external_id": "staff_8842"
                    },
                    "reason": "client_request"
                  }
                },
                "organisation": {
                  "summary": "A whole-organisation export for a migration",
                  "value": {
                    "scope": "organisation",
                    "include": [
                      "*"
                    ],
                    "format": "bundle",
                    "requested_by": {
                      "external_id": "staff_0001"
                    },
                    "reason": "migration"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. Exports are jobs.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Export"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The requester has no standing to make this disclosure.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/exports/{exportId}": {
      "parameters": [
        {
          "name": "exportId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getExport",
        "summary": "Retrieve an export",
        "tags": [
          "Exports"
        ],
        "responses": {
          "200": {
            "description": "The export.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Export"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/exports/{exportId}/download": {
      "parameters": [
        {
          "name": "exportId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "downloadExport",
        "summary": "Download a completed export",
        "description": "Available for seven days after completion, then deleted. A large\norganisation-scoped export is delivered as independently downloadable parts,\nlisted in the top-level manifest.\n",
        "tags": [
          "Exports"
        ],
        "responses": {
          "200": {
            "description": "One or more download links.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "parts": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "url": {
                                    "type": "string",
                                    "format": "uri"
                                  },
                                  "part": {
                                    "type": "integer"
                                  },
                                  "size_bytes": {
                                    "type": "integer"
                                  }
                                }
                              }
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The export is not complete yet.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/notes": {
      "get": {
        "operationId": "listNotes",
        "summary": "List clinical notes",
        "description": "Read-only, and available only to organisations with the notes capability enabled.\nThere is no write endpoint on this API. That is a product decision, not a gap:\nyour platform owns clinical documentation.\n",
        "tags": [
          "Notes"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/filterSubject"
          },
          {
            "name": "session",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "finalised_after",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Notes.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Note"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The notes capability is not enabled for this organisation.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/notes/templates": {
      "get": {
        "operationId": "listNoteTemplates",
        "summary": "List documentation styles",
        "tags": [
          "Notes"
        ],
        "responses": {
          "200": {
            "description": "Templates.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/NoteTemplate"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/notes/{noteId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/noteId"
        }
      ],
      "get": {
        "operationId": "getNote",
        "summary": "Retrieve the active version of a note",
        "tags": [
          "Notes"
        ],
        "responses": {
          "200": {
            "description": "The note.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Note"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/notes/{noteId}/versions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/noteId"
        }
      ],
      "get": {
        "operationId": "listNoteVersions",
        "summary": "List a note's version history",
        "tags": [
          "Notes"
        ],
        "responses": {
          "200": {
            "description": "Versions, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/NoteVersion"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/changes": {
      "get": {
        "operationId": "listChanges",
        "summary": "Read the change feed",
        "description": "One ordered, resumable feed of everything that changed in your organisation.\nThere are no webhooks; you poll this on whatever interval suits you.\n\nThe cursor is opaque and monotonic — store it, resume from it, and you cannot\nmiss an entry or process one twice. Entries are retained thirty days. A poll that\nfinds nothing is cheap and does not count against your rate limit.\n\nPayloads carry identifiers, statuses, counts and timestamps. Never a name, never\nclinical content, never a page of story text or a form answer. Fetch the object\nif you need it.\n",
        "tags": [
          "Changes"
        ],
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "description": "The cursor from your last read. Omit to start from now.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "types",
            "in": "query",
            "description": "Comma-delimited change types to include. Omit for all.",
            "schema": {
              "type": "string",
              "examples": [
                "artifact.ready,session.complete"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Changes, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Change"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "description": "The cursor is unrecognised or older than the retention window.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Retrieve generation quota consumption",
        "description": "Build a warning into your own interface at eighty percent. A clinician who\ndiscovers an exhausted quota halfway through a session with a seven-year-old will\ncontact you, not us.\n",
        "tags": [
          "Usage"
        ],
        "responses": {
          "200": {
            "description": "This period's consumption.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/Usage"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/usage/by-clinician": {
      "get": {
        "operationId": "getUsageByClinician",
        "summary": "Retrieve per-clinician generation consumption",
        "tags": [
          "Usage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/filterCreatedBy"
          }
        ],
        "responses": {
          "200": {
            "description": "Consumption per clinician.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/PaginatedEnvelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "clinician": {
                                "$ref": "#/components/schemas/Reference"
                              },
                              "counts": {
                                "type": "object",
                                "additionalProperties": {
                                  "type": "integer"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/usage/history": {
      "get": {
        "operationId": "getUsageHistory",
        "summary": "Retrieve previous periods' consumption",
        "tags": [
          "Usage"
        ],
        "parameters": [
          {
            "name": "periods",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 24,
              "default": 6
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Historical consumption, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Envelope"
                    },
                    {
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Usage"
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "An access token from `POST /oauth/token`. Server-side only — a browser surface is\nauthorised by a session token from `POST /tokens` instead.\n"
      }
    },
    "parameters": {
      "cursor": {
        "name": "cursor",
        "in": "query",
        "description": "From `meta.pagination.next_cursor` on the previous page.",
        "schema": {
          "type": "string"
        }
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      },
      "includeArchived": {
        "name": "include_archived",
        "in": "query",
        "description": "Archived records are excluded by default.",
        "schema": {
          "type": "boolean",
          "default": false
        }
      },
      "idempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "A unique key for this operation, typically a version-4 identifier. A repeat with\nthe same key and body replays the original response; the same key with a\ndifferent body is rejected. Retained 24 hours.\n",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 255
        }
      },
      "filterSubject": {
        "name": "subject",
        "in": "query",
        "description": "A PlaySpace or `ext:`-prefixed patient identifier.",
        "schema": {
          "type": "string"
        }
      },
      "filterCreatedBy": {
        "name": "created_by",
        "in": "query",
        "description": "A PlaySpace or `ext:`-prefixed practitioner identifier.",
        "schema": {
          "type": "string"
        }
      },
      "clinicId": {
        "name": "clinicId",
        "in": "path",
        "required": true,
        "description": "A PlaySpace identifier, or yours prefixed `ext:`.",
        "schema": {
          "type": "string",
          "examples": [
            "clin_5Yt2Rm",
            "ext:location_northgate"
          ]
        }
      },
      "practitionerId": {
        "name": "practitionerId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "prac_4Kx8Wq",
            "ext:staff_8842"
          ]
        }
      },
      "patientId": {
        "name": "patientId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "pt_9Kd2mXwF",
            "ext:client_55130"
          ]
        }
      },
      "sessionId": {
        "name": "sessionId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "examples": [
            "sess_2Nk8pQvR7xLm",
            "ext:appt_11923"
          ]
        }
      },
      "storybookId": {
        "name": "storybookId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "worksheetId": {
        "name": "worksheetId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "formId": {
        "name": "formId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "noteId": {
        "name": "noteId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "XRequestId": {
        "description": "Log this. Quoting it addresses a single request in our audit history.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimitMinuteRemaining": {
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitMinuteReset": {
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "RateLimitHourRemaining": {
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitHourReset": {
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, malformed or expired credentials.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://api.playspace.health/problems/unauthorized",
              "title": "Unauthorized",
              "status": 401,
              "request_id": "req_8Kp2mQ"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Authenticated but not permitted — a missing scope, an insufficient role, a\nsurface the organisation is not licensed for, or a suspended organisation.\n",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotFound": {
        "description": "Absent, archived, or belonging to another organisation. The three are\ndeliberately indistinguishable so a caller cannot map what it cannot reach.\n",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Conflict": {
        "description": "An idempotency conflict, or a genuine collision such as a double-booked room.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "ValidationError": {
        "description": "The request was well-formed but its contents are not acceptable.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://api.playspace.health/problems/validation-error",
              "title": "Validation failed",
              "status": 422,
              "detail": "One or more fields are invalid.",
              "fields": [
                {
                  "path": "pages",
                  "message": "must be between 4 and 24"
                }
              ],
              "request_id": "req_8Kp2mQ"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "X-RateLimit-Minute-Remaining": {
            "$ref": "#/components/headers/RateLimitMinuteRemaining"
          },
          "X-RateLimit-Hour-Remaining": {
            "$ref": "#/components/headers/RateLimitHourRemaining"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "QuotaExceeded": {
        "description": "The generation allowance for this period is exhausted. Distinct from a rate limit:\nwaiting will not help until `resets_at`.\n",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "https://api.playspace.health/problems/quota-exceeded",
              "title": "Generation quota exceeded",
              "status": 429,
              "detail": "The storybook generation allowance for this period is exhausted.",
              "resource": "storybooks",
              "resets_at": "2026-10-01T00:00:00Z",
              "request_id": "req_8Kp2mQ"
            }
          }
        }
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457. Extensions are spread onto the top level.",
        "required": [
          "type",
          "title",
          "status"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "A stable identifier under `https://api.playspace.health/problems/`. The set\nonly grows and an existing slug never changes meaning, so it is safe to\nbranch on.\n"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          },
          "fields": {
            "type": "array",
            "description": "Present on `validation-error`.",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "resets_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Present on `quota-exceeded`."
          }
        },
        "additionalProperties": true
      },
      "Envelope": {
        "type": "object",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {},
          "meta": {
            "$ref": "#/components/schemas/Meta"
          }
        }
      },
      "PaginatedEnvelope": {
        "type": "object",
        "required": [
          "data",
          "meta"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {}
          },
          "meta": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Meta"
              },
              {
                "type": "object",
                "required": [
                  "pagination"
                ],
                "properties": {
                  "pagination": {
                    "$ref": "#/components/schemas/Pagination"
                  }
                }
              }
            ]
          }
        }
      },
      "Meta": {
        "type": "object",
        "required": [
          "request_id",
          "generated_at"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "examples": [
              "req_8Kp2mQ"
            ]
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "Pagination": {
        "type": "object",
        "required": [
          "limit",
          "has_more"
        ],
        "properties": {
          "cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "limit": {
            "type": "integer"
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "Ref": {
        "description": "A reference to a resource by either identifier. Supply exactly one of `id` or\n`external_id`.\n",
        "oneOf": [
          {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "string"
              }
            },
            "additionalProperties": false
          },
          {
            "type": "object",
            "required": [
              "external_id"
            ],
            "properties": {
              "external_id": {
                "type": "string"
              }
            },
            "additionalProperties": false
          }
        ]
      },
      "Reference": {
        "type": "object",
        "description": "A resolved pointer, returned on every object that references another.",
        "properties": {
          "id": {
            "type": "string"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Surface": {
        "type": "string",
        "enum": [
          "sandtray",
          "dollhouse",
          "whiteboard",
          "games",
          "playroom",
          "storybooks",
          "worksheets",
          "forms",
          "studio",
          "models"
        ]
      },
      "Role": {
        "type": "string",
        "enum": [
          "clinician",
          "patient"
        ]
      },
      "PractitionerRole": {
        "type": "string",
        "enum": [
          "member",
          "administrator",
          "owner"
        ]
      },
      "CaregiverRelationship": {
        "type": "string",
        "enum": [
          "parent",
          "guardian",
          "foster_carer",
          "grandparent",
          "sibling",
          "other"
        ]
      },
      "PlayroomType": {
        "type": "string",
        "enum": [
          "child",
          "teen",
          "adult"
        ]
      },
      "SessionItem": {
        "type": "string",
        "enum": [
          "activity_shelf",
          "multiplayer_games",
          "single_player_games",
          "wall_posters",
          "whiteboard",
          "sand_tray",
          "dollhouse"
        ]
      },
      "GameProvider": {
        "type": "string",
        "enum": [
          "playspace",
          "foony",
          "papergames",
          "studio"
        ]
      },
      "ContentStatus": {
        "type": "string",
        "enum": [
          "generating",
          "ready",
          "failed",
          "archived"
        ]
      },
      "PublishTarget": {
        "type": "string",
        "enum": [
          "false",
          "playroom_toolkit",
          "clinic_library",
          "community"
        ]
      },
      "SessionStatus": {
        "type": "string",
        "enum": [
          "pending",
          "live",
          "complete",
          "cancelled"
        ]
      },
      "JobStatus": {
        "type": "string",
        "enum": [
          "queued",
          "running",
          "complete",
          "failed",
          "cancelled"
        ]
      },
      "JobType": {
        "type": "string",
        "enum": [
          "storybook",
          "storybook_page",
          "worksheet",
          "form",
          "form_extraction",
          "game",
          "model",
          "export"
        ]
      },
      "ArtifactType": {
        "type": "string",
        "enum": [
          "storybook",
          "storybook_copy",
          "worksheet",
          "worksheet_copy",
          "form",
          "form_response",
          "sandtray_save",
          "dollhouse_save",
          "whiteboard_snapshot",
          "generated_game",
          "model",
          "session_summary",
          "note"
        ]
      },
      "DownloadFormat": {
        "type": "string",
        "enum": [
          "pdf",
          "epub",
          "png",
          "svg",
          "glb",
          "zip",
          "csv",
          "json"
        ]
      },
      "NotifyFlags": {
        "type": "object",
        "description": "Not persisted. Controls whether this write sends an email for this write only.\n",
        "properties": {
          "patient": {
            "type": "boolean",
            "default": false
          },
          "clinician": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "Organisation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "suspended",
              "revoked"
            ]
          },
          "capabilities": {
            "type": "array",
            "description": "Product areas enabled for this organisation, such as `notes`.",
            "items": {
              "type": "string"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PracticeGroup": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "parent": {
            "$ref": "#/components/schemas/Reference"
          },
          "clinic_count": {
            "type": "integer"
          }
        }
      },
      "Clinic": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "clin_5Yt2Rm"
            ]
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "location_northgate"
            ]
          },
          "name": {
            "type": "string"
          },
          "country": {
            "type": "string",
            "description": "Two-letter country code."
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "group": {
            "$ref": "#/components/schemas/Reference"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "archived_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ClinicWrite": {
        "type": "object",
        "required": [
          "external_id",
          "name",
          "country"
        ],
        "properties": {
          "external_id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2
          },
          "timezone": {
            "type": "string"
          },
          "group": {
            "$ref": "#/components/schemas/Ref"
          }
        }
      },
      "Practitioner": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "prac_4Kx8Wq"
            ]
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "staff_8842"
            ]
          },
          "clinic": {
            "$ref": "#/components/schemas/Reference"
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "role": {
            "$ref": "#/components/schemas/PractitionerRole"
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "archived_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "PractitionerWrite": {
        "type": "object",
        "required": [
          "external_id",
          "clinic",
          "first_name",
          "last_name"
        ],
        "properties": {
          "external_id": {
            "type": "string"
          },
          "clinic": {
            "$ref": "#/components/schemas/Ref"
          },
          "first_name": {
            "type": "string",
            "maxLength": 100
          },
          "last_name": {
            "type": "string",
            "maxLength": 100
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "role": {
            "$ref": "#/components/schemas/PractitionerRole"
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "Required for regional availability of some surfaces. Omit it and those\nsurfaces fail closed for this practitioner.\n"
          }
        }
      },
      "Patient": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "pt_9Kd2mXwF"
            ]
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "client_55130"
            ]
          },
          "clinic": {
            "$ref": "#/components/schemas/Reference"
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "date_of_birth": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "caregivers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CaregiverLink"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "archived_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "PatientWrite": {
        "type": "object",
        "required": [
          "external_id",
          "clinic",
          "first_name",
          "last_name"
        ],
        "properties": {
          "external_id": {
            "type": "string"
          },
          "clinic": {
            "$ref": "#/components/schemas/Ref"
          },
          "first_name": {
            "type": "string",
            "maxLength": 100,
            "description": "An initial is acceptable. Send the minimum a clinician needs to recognise the right person."
          },
          "last_name": {
            "type": "string",
            "maxLength": 100
          },
          "date_of_birth": {
            "type": "string",
            "format": "date"
          },
          "practitioners": {
            "type": "array",
            "description": "Clinicians whose caseload this client is on.",
            "items": {
              "$ref": "#/components/schemas/Ref"
            }
          },
          "caregivers": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "external_id",
                "relationship"
              ],
              "properties": {
                "external_id": {
                  "type": "string"
                },
                "relationship": {
                  "$ref": "#/components/schemas/CaregiverRelationship"
                }
              }
            }
          }
        }
      },
      "CaregiverLink": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "patient": {
            "$ref": "#/components/schemas/Reference"
          },
          "caregiver": {
            "$ref": "#/components/schemas/Reference"
          },
          "relationship": {
            "$ref": "#/components/schemas/CaregiverRelationship"
          },
          "receives_session_links": {
            "type": "boolean"
          },
          "can_request_export": {
            "type": "boolean"
          }
        }
      },
      "Session": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "sess_2Nk8pQvR7xLm"
            ]
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "appt_11923"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/SessionStatus"
          },
          "clinician": {
            "$ref": "#/components/schemas/Reference"
          },
          "participants": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "patient": {
                  "$ref": "#/components/schemas/Reference"
                },
                "joined_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "playroom": {
            "type": [
              "string",
              "null"
            ]
          },
          "surfaces": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Surface"
            }
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "ended_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "links": {
            "description": "Present on create and on an explicit re-mint. Never stored, never returned on a plain get.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/SessionLinks"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SessionCreate": {
        "type": "object",
        "required": [
          "appointment",
          "clinician",
          "participants"
        ],
        "properties": {
          "appointment": {
            "type": "object",
            "required": [
              "external_id"
            ],
            "properties": {
              "external_id": {
                "type": "string"
              },
              "scheduled_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "clinician": {
            "$ref": "#/components/schemas/Ref"
          },
          "participants": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": [
                "patient"
              ],
              "properties": {
                "patient": {
                  "$ref": "#/components/schemas/Ref"
                }
              }
            }
          },
          "playroom": {
            "type": "string",
            "description": "A playroom identifier or slug. Omit for the clinic's default."
          },
          "surfaces": {
            "type": "array",
            "description": "The ceiling for this session. Omit for every licensed surface.",
            "items": {
              "$ref": "#/components/schemas/Surface"
            }
          },
          "notify": {
            "$ref": "#/components/schemas/NotifyFlags"
          }
        }
      },
      "SessionLinks": {
        "type": "object",
        "properties": {
          "clinician": {
            "type": "string",
            "format": "uri"
          },
          "patient": {
            "type": "string",
            "format": "uri"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SessionSummary": {
        "type": "object",
        "properties": {
          "session": {
            "$ref": "#/components/schemas/Reference"
          },
          "duration_ms": {
            "type": "integer"
          },
          "participants": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "role": {
                  "$ref": "#/components/schemas/Role"
                },
                "joined_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "left_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "surfaces_used": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "surface": {
                  "$ref": "#/components/schemas/Surface"
                },
                "duration_ms": {
                  "type": "integer"
                }
              }
            }
          },
          "games_played": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string",
                  "description": "The catalog product name. Never patient text",
                  "never a URL.": null
                },
                "duration_ms": {
                  "type": "integer",
                  "description": "An upper bound — a backgrounded tab keeps accruing."
                },
                "plays": {
                  "type": "integer"
                }
              }
            }
          },
          "artifacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Artifact"
            }
          }
        }
      },
      "TokenCreate": {
        "type": "object",
        "required": [
          "session",
          "subject",
          "role",
          "origins"
        ],
        "properties": {
          "session": {
            "type": "string"
          },
          "subject": {
            "$ref": "#/components/schemas/Ref"
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          },
          "surfaces": {
            "type": "array",
            "description": "Never wider than the session's ceiling. Omit to inherit it.",
            "items": {
              "$ref": "#/components/schemas/Surface"
            }
          },
          "origins": {
            "type": "array",
            "minItems": 1,
            "description": "Host origins allowed to frame this surface. Validated at mint, so a typo is a\nreadable error rather than a blank frame in production.\n",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "ttl": {
            "type": "string",
            "default": "15m",
            "description": "Maximum `60m`. Short by design; the SDK renews silently.",
            "examples": [
              "15m",
              "60m"
            ]
          }
        }
      },
      "SessionToken": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          },
          "surfaces": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Surface"
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Playroom": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/PlayroomType"
          },
          "theme": {
            "type": [
              "string",
              "null"
            ]
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SessionItem"
            }
          },
          "colour_palette": {
            "type": [
              "string",
              "null"
            ]
          },
          "contents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlayroomContent"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PlayroomWrite": {
        "type": "object",
        "required": [
          "title",
          "type"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "type": {
            "$ref": "#/components/schemas/PlayroomType"
          },
          "theme": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SessionItem"
            }
          },
          "colour_palette": {
            "type": "string"
          },
          "contents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContentRef"
            }
          }
        }
      },
      "PlayroomContent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "storybook",
              "worksheet",
              "form",
              "game",
              "generated_game"
            ]
          },
          "ref": {
            "$ref": "#/components/schemas/Reference"
          },
          "title": {
            "type": "string"
          },
          "order": {
            "type": "integer"
          }
        }
      },
      "ContentRef": {
        "type": "object",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "storybook",
              "worksheet",
              "form",
              "game",
              "generated_game"
            ]
          },
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "description": "For catalog games."
          },
          "order": {
            "type": "integer"
          }
        }
      },
      "Toolkit": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "contents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlayroomContent"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ToolkitWrite": {
        "type": "object",
        "required": [
          "title"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "description": {
            "type": "string"
          },
          "contents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContentRef"
            }
          }
        }
      },
      "Game": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "examples": [
              "worry-pet"
            ]
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "provider": {
            "$ref": "#/components/schemas/GameProvider"
          },
          "min_players": {
            "type": "integer"
          },
          "max_players": {
            "type": "integer"
          },
          "skills": {
            "type": "array",
            "description": "Therapeutic skills the game develops.",
            "items": {
              "type": "string"
            }
          },
          "age_range": {
            "type": "array",
            "maxItems": 2,
            "minItems": 2,
            "items": {
              "type": "integer"
            }
          },
          "thumbnail_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "embeddable": {
            "type": "boolean"
          }
        }
      },
      "Storybook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "sb_7Hn3xQ"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/ContentStatus"
          },
          "page_count": {
            "type": "integer"
          },
          "age_group": {
            "type": [
              "string",
              "null"
            ]
          },
          "reading_level": {
            "type": [
              "string",
              "null"
            ]
          },
          "style": {
            "type": [
              "string",
              "null"
            ]
          },
          "published": {
            "$ref": "#/components/schemas/PublishTarget"
          },
          "created_by": {
            "$ref": "#/components/schemas/Reference"
          },
          "subject": {
            "description": "Null for library content that is not about anyone in particular.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "session": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "characters": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StorybookCharacter"
            }
          },
          "pages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StorybookPage"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "StorybookCharacter": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "protagonist",
              "supporting",
              "background"
            ]
          },
          "description": {
            "type": "string",
            "description": "The visual description carried into every page's image generation."
          }
        }
      },
      "StorybookPage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "number": {
            "type": "integer"
          },
          "text": {
            "type": "string"
          },
          "image_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "characters_in_scene": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "regenerating": {
            "type": "boolean"
          }
        }
      },
      "StorybookGenerate": {
        "type": "object",
        "required": [
          "clinician",
          "prompt"
        ],
        "properties": {
          "clinician": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Ref"
              }
            ],
            "description": "Required. Generation costs money and somebody owns that."
          },
          "subject": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Ref"
              }
            ],
            "description": "Set it and the storybook is personal. Omit it and you have made library content."
          },
          "session": {
            "$ref": "#/components/schemas/Ref"
          },
          "prompt": {
            "type": "string",
            "minLength": 10,
            "maxLength": 2000
          },
          "pages": {
            "type": "integer",
            "minimum": 4,
            "maximum": 24,
            "default": 8
          },
          "age_group": {
            "type": "string",
            "examples": [
              "3-5",
              "5-8",
              "9-12",
              "13-17"
            ]
          },
          "reading_level": {
            "type": "string",
            "examples": [
              "pre-reader",
              "grade-1",
              "grade-2",
              "grade-4"
            ]
          },
          "style": {
            "type": "string",
            "examples": [
              "watercolour",
              "cartoon",
              "storybook-classic",
              "collage"
            ]
          },
          "characters": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "$ref": "#/components/schemas/StorybookCharacter"
            }
          }
        }
      },
      "Worksheet": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "ws_3Bn8kR"
            ]
          },
          "title": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/ContentStatus"
          },
          "page_count": {
            "type": "integer"
          },
          "age_group": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_by": {
            "$ref": "#/components/schemas/Reference"
          },
          "subject": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "published": {
            "$ref": "#/components/schemas/PublishTarget"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WorksheetWrite": {
        "type": "object",
        "required": [
          "title"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "age_group": {
            "type": "string"
          },
          "published": {
            "$ref": "#/components/schemas/PublishTarget"
          }
        }
      },
      "WorksheetGenerate": {
        "type": "object",
        "required": [
          "clinician",
          "prompt"
        ],
        "properties": {
          "clinician": {
            "$ref": "#/components/schemas/Ref"
          },
          "subject": {
            "$ref": "#/components/schemas/Ref"
          },
          "session": {
            "$ref": "#/components/schemas/Ref"
          },
          "prompt": {
            "type": "string",
            "minLength": 10,
            "maxLength": 2000
          },
          "pages": {
            "type": "integer",
            "minimum": 1,
            "maximum": 12,
            "default": 3
          },
          "age_group": {
            "type": "string"
          },
          "include_imagery": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "WorksheetCopy": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "worksheet": {
            "$ref": "#/components/schemas/Reference"
          },
          "subject": {
            "$ref": "#/components/schemas/Reference"
          },
          "created_by": {
            "$ref": "#/components/schemas/Reference"
          },
          "session": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "assigned",
              "in_progress",
              "complete"
            ]
          },
          "due_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Form": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "frm_2Kd9Wp"
            ]
          },
          "title": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/ContentStatus"
          },
          "scored": {
            "type": "boolean"
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FormField"
            }
          },
          "created_by": {
            "$ref": "#/components/schemas/Reference"
          },
          "published": {
            "$ref": "#/components/schemas/PublishTarget"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FormField": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "short_text",
              "long_text",
              "number",
              "single_select",
              "multi_select",
              "scale",
              "date",
              "signature",
              "file",
              "image_choice"
            ]
          },
          "required": {
            "type": "boolean"
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scale": {
            "type": "object",
            "properties": {
              "min": {
                "type": "integer"
              },
              "max": {
                "type": "integer"
              },
              "labels": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "conditional": {
            "description": "Show this field only when another field holds a given value.",
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "field_id": {
                "type": "string"
              },
              "equals": {}
            }
          }
        }
      },
      "FormWrite": {
        "type": "object",
        "required": [
          "title",
          "fields"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "scored": {
            "type": "boolean",
            "default": false
          },
          "fields": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/FormField"
            }
          },
          "published": {
            "$ref": "#/components/schemas/PublishTarget"
          }
        }
      },
      "FormResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "form": {
            "$ref": "#/components/schemas/Reference"
          },
          "subject": {
            "$ref": "#/components/schemas/Reference"
          },
          "submitted_by": {
            "description": "The client, a caregiver, or the clinician.",
            "type": "object",
            "properties": {
              "role": {
                "type": "string",
                "enum": [
                  "patient",
                  "caregiver",
                  "clinician"
                ]
              },
              "ref": {
                "$ref": "#/components/schemas/Reference"
              }
            }
          },
          "session": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "answers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field_id": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "value": {}
              }
            }
          },
          "score": {
            "type": [
              "number",
              "null"
            ],
            "description": "Present for scored instruments only."
          },
          "submitted_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "GeneratedGame": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/ContentStatus"
          },
          "players": {
            "type": "integer"
          },
          "play_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "created_by": {
            "$ref": "#/components/schemas/Reference"
          },
          "subject": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "published": {
            "$ref": "#/components/schemas/PublishTarget"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Model": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/ContentStatus"
          },
          "format": {
            "type": "string",
            "const": "glb"
          },
          "preview_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "source": {
            "type": "string",
            "enum": [
              "text",
              "image"
            ]
          },
          "created_by": {
            "$ref": "#/components/schemas/Reference"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Job": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "job_5Rp1wKz"
            ]
          },
          "type": {
            "$ref": "#/components/schemas/JobType"
          },
          "status": {
            "$ref": "#/components/schemas/JobStatus"
          },
          "progress": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "estimated_seconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "result": {
            "description": "Present when `status` is `complete`.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Artifact"
              },
              {
                "type": "null"
              }
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Present when `status` is `failed`. Written for a clinician rather than an\nengineer. A failed job does not consume quota.\n"
          },
          "created_by": {
            "$ref": "#/components/schemas/Reference"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Artifact": {
        "type": "object",
        "description": "Everything PlaySpace produced and kept. The attribution triple —\n`created_by`, `subject`, `session` — is what makes the client library, the export\nbundle, the spend report and the session summary all one query.\n",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/ArtifactType"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/ContentStatus"
          },
          "created_by": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Reference"
              }
            ],
            "description": "The clinician. Always present."
          },
          "subject": {
            "description": "The client it was made for. Null for library content.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "session": {
            "description": "Where it happened. Null for content created outside a session.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "formats": {
            "type": "array",
            "description": "Download formats available for this artifact.",
            "items": {
              "$ref": "#/components/schemas/DownloadFormat"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ExportCreate": {
        "type": "object",
        "required": [
          "include",
          "requested_by",
          "reason"
        ],
        "properties": {
          "subject": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Ref"
              }
            ],
            "description": "Required unless `scope` is `organisation`."
          },
          "scope": {
            "type": "string",
            "enum": [
              "subject",
              "organisation"
            ],
            "default": "subject"
          },
          "include": {
            "type": "array",
            "minItems": 1,
            "description": "Artifact families, or `*` for everything the organisation is licensed for and\nentitled to read. Naming them explicitly is better practice — an export is a\ndisclosure, and disclosures should be deliberate.\n",
            "items": {
              "type": "string",
              "enum": [
                "*",
                "storybooks",
                "worksheets",
                "form_responses",
                "sandtray_saves",
                "dollhouse_saves",
                "whiteboard_snapshots",
                "generated_games",
                "models",
                "session_summaries",
                "notes"
              ]
            }
          },
          "format": {
            "type": "string",
            "enum": [
              "bundle",
              "json"
            ],
            "default": "bundle"
          },
          "requested_by": {
            "$ref": "#/components/schemas/Ref"
          },
          "reason": {
            "type": "string",
            "enum": [
              "client_request",
              "caregiver_request",
              "clinical_transfer",
              "migration",
              "legal",
              "internal_review"
            ]
          }
        }
      },
      "Export": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "scope": {
            "type": "string",
            "enum": [
              "subject",
              "organisation"
            ]
          },
          "subject": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "include": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "format": {
            "type": "string",
            "enum": [
              "bundle",
              "json"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "complete",
              "failed",
              "expired"
            ]
          },
          "progress": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "size_bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "part_count": {
            "type": "integer",
            "default": 1
          },
          "requested_by": {
            "$ref": "#/components/schemas/Reference"
          },
          "reason": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Seven days after completion. Request it again if you need it again."
          }
        }
      },
      "Note": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "subject": {
            "$ref": "#/components/schemas/Reference"
          },
          "author": {
            "$ref": "#/components/schemas/Reference"
          },
          "session": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "template": {
            "$ref": "#/components/schemas/Reference"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "final",
              "amended"
            ]
          },
          "version": {
            "type": "integer"
          },
          "sections": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "heading": {
                  "type": "string"
                },
                "body": {
                  "type": "string"
                }
              }
            }
          },
          "finalised_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "NoteVersion": {
        "type": "object",
        "properties": {
          "version": {
            "type": "integer"
          },
          "author": {
            "$ref": "#/components/schemas/Reference"
          },
          "is_active": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "NoteTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "sections": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Change": {
        "type": "object",
        "properties": {
          "sequence": {
            "type": "integer",
            "description": "Monotonic within your organisation."
          },
          "type": {
            "$ref": "#/components/schemas/ChangeType"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "artifact": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "session": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "subject": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "job": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Reference"
              },
              {
                "type": "null"
              }
            ]
          },
          "data": {
            "type": "object",
            "description": "Type-specific detail. Identifiers, statuses, counts, durations and enumerated\nvalues only — never a name, never clinical content.\n",
            "additionalProperties": true
          }
        }
      },
      "ChangeType": {
        "type": "string",
        "enum": [
          "session.created",
          "session.started",
          "session.complete",
          "session.cancelled",
          "artifact.created",
          "artifact.ready",
          "artifact.deleted",
          "job.progress",
          "job.complete",
          "job.failed",
          "form.submitted",
          "worksheet.completed",
          "note.available",
          "export.ready",
          "quota.threshold"
        ]
      },
      "Usage": {
        "type": "object",
        "properties": {
          "period": {
            "type": "string",
            "examples": [
              "2026-09"
            ]
          },
          "resources": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "used": {
                  "type": "integer"
                },
                "limit": {
                  "type": "integer"
                }
              }
            },
            "examples": [
              {
                "storybooks": {
                  "used": 214,
                  "limit": 1000
                },
                "worksheets": {
                  "used": 88,
                  "limit": 500
                },
                "forms": {
                  "used": 41,
                  "limit": 500
                },
                "games": {
                  "used": 12,
                  "limit": 100
                },
                "models": {
                  "used": 37,
                  "limit": 250
                }
              }
            ]
          },
          "resets_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}
