{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Cubby Corner Partner API",
    "version": "1.0.0-preview.1",
    "description": "Fictional-data partner sandbox. No real child data, clinical claims, production accounts, payments, OAuth, SMART, or FHIR endpoints. Parental consent and verified chart mapping are required for real integrations. Cubby is independent of Trellis."
  },
  "servers": [
    {
      "url": "https://developers.cubby.3-21-209-244.sslip.io/api/v1",
      "description": "Provisioned partner sandbox, fictional data only"
    },
    {
      "url": "http://127.0.0.1:19043/api/v1",
      "description": "Local development preview"
    }
  ],
  "paths": {
    "/catalog": {
      "get": {
        "operationId": "catalog",
        "summary": "List released games and allowed settings",
        "tags": [
          "Partner API"
        ],
        "security": [],
        "x-cubby-scopes": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Catalog"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/integration": {
      "get": {
        "operationId": "integration",
        "summary": "Inspect this key's clinic, permissions, and expiry",
        "tags": [
          "Partner API"
        ],
        "security": [
          {
            "CubbyKey": []
          }
        ],
        "x-cubby-scopes": [],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Integration"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/clinics/{clinicId}": {
      "parameters": [
        {
          "name": "clinicId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          }
        }
      ],
      "get": {
        "operationId": "clinicDashboard",
        "summary": "List active connections and permitted plan/report metadata",
        "tags": [
          "Partner API"
        ],
        "security": [
          {
            "CubbyKey": []
          }
        ],
        "x-cubby-scopes": [
          "connections:read"
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dashboard"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/clinics/{clinicId}/events": {
      "parameters": [
        {
          "name": "clinicId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          }
        }
      ],
      "get": {
        "operationId": "events",
        "summary": "Poll events in stable insertion order",
        "tags": [
          "Partner API"
        ],
        "security": [
          {
            "CubbyKey": []
          }
        ],
        "x-cubby-scopes": [
          "events:read"
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventPage"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 32,
              "pattern": "^[a-f0-9]{32}$"
            },
            "description": "Last committed event ID from this clinic. Missing means the first page."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 100
            }
          }
        ]
      }
    },
    "/connections/{connectionId}/assignments": {
      "parameters": [
        {
          "name": "connectionId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          }
        }
      ],
      "post": {
        "operationId": "assignPlan",
        "summary": "Create or revise a plan for parent review",
        "tags": [
          "Partner API"
        ],
        "security": [
          {
            "CubbyKey": []
          }
        ],
        "x-cubby-scopes": [
          "plans:write"
        ],
        "responses": {
          "201": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Assignment"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 64,
              "pattern": "^[A-Za-z0-9_-]{1,64}$"
            },
            "description": "Persist one key per approved write. Retry identical content with the same key; different content returns 409."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlanInput"
              }
            }
          }
        }
      }
    },
    "/assignments/{assignmentId}": {
      "parameters": [
        {
          "name": "assignmentId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          }
        }
      ],
      "get": {
        "operationId": "assignment",
        "summary": "Read the current plan revision and family acceptance state",
        "tags": [
          "Partner API"
        ],
        "security": [
          {
            "CubbyKey": []
          }
        ],
        "x-cubby-scopes": [
          "plans:read"
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Assignment"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/assignments/{assignmentId}/download": {
      "parameters": [
        {
          "name": "assignmentId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          }
        }
      ],
      "get": {
        "operationId": "downloadPlan",
        "summary": "Download a family-accepted current plan",
        "tags": [
          "Partner API"
        ],
        "security": [
          {
            "CubbyKey": []
          }
        ],
        "x-cubby-scopes": [
          "plans:read"
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayPlan"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/reports/{reportId}": {
      "parameters": [
        {
          "name": "reportId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          }
        }
      ],
      "get": {
        "operationId": "report",
        "summary": "Read an immutable family snapshot, provenance, and separate reviews",
        "tags": [
          "Partner API"
        ],
        "security": [
          {
            "CubbyKey": []
          }
        ],
        "x-cubby-scopes": [
          "reports:read"
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Report"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/reports/{reportId}/review": {
      "parameters": [
        {
          "name": "reportId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          }
        }
      ],
      "post": {
        "operationId": "reviewReport",
        "summary": "Save a separate clinician review note",
        "tags": [
          "Partner API"
        ],
        "security": [
          {
            "CubbyKey": []
          }
        ],
        "x-cubby-scopes": [
          "reports:review"
        ],
        "responses": {
          "201": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Review"
                }
              }
            }
          },
          "400": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Request rejected or could not finish",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 64,
              "pattern": "^[A-Za-z0-9_-]{1,64}$"
            },
            "description": "Persist one key per approved write. Retry identical content with the same key; different content returns 409."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "note": {
                    "type": "string",
                    "maxLength": 400,
                    "minLength": 1
                  }
                },
                "required": [
                  "note"
                ],
                "additionalProperties": false
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "CubbyKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque clinic-scoped integration key",
        "description": "Issued by a clinic owner or sandbox operator. Keep server-side. Expiry: 90 days. Revocation is immediate."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      },
      "Integration": {
        "type": "object",
        "properties": {
          "clinicId": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "mode": {
            "const": "fictional-data-only"
          },
          "expires": {
            "type": "integer",
            "minimum": 1
          }
        },
        "required": [
          "clinicId",
          "scopes",
          "mode",
          "expires"
        ],
        "additionalProperties": false
      },
      "Game": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "zone": {
            "type": "string"
          },
          "maxLevel": {
            "type": "integer",
            "minimum": 1
          },
          "minAge": {
            "type": "integer",
            "minimum": 0
          },
          "settings": {
            "type": "object"
          }
        },
        "required": [
          "id",
          "title",
          "zone",
          "maxLevel",
          "minAge"
        ],
        "additionalProperties": false
      },
      "Catalog": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string"
          },
          "games": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Game"
            }
          }
        },
        "required": [
          "version",
          "games"
        ],
        "additionalProperties": false
      },
      "PlanItem": {
        "type": "object",
        "properties": {
          "g": {
            "enum": [
              "animal-moves",
              "freeze-dance",
              "flamingo-postcards",
              "puddle-lanterns",
              "berry-pinch",
              "honey-trail",
              "firefly-catch",
              "get-dressed",
              "animal-echo",
              "cubby-says",
              "silly-faces",
              "silly-plate",
              "taste-trail",
              "wave-breaths"
            ]
          },
          "times": {
            "type": "integer",
            "minimum": 1,
            "maximum": 14
          },
          "per": {
            "enum": [
              "day",
              "week"
            ]
          },
          "why": {
            "type": "string",
            "maxLength": 160
          },
          "level": {
            "type": "integer",
            "minimum": 1
          },
          "state": {
            "enum": [
              "on",
              "off",
              "today"
            ]
          },
          "settings": {
            "type": "object",
            "properties": {
              "pace": {
                "enum": [
                  "normal",
                  "slow"
                ]
              },
              "targets": {
                "type": "array",
                "items": {
                  "type": "string",
                  "maxLength": 60
                },
                "minItems": 1,
                "maxItems": 12
              }
            },
            "required": [],
            "additionalProperties": false
          }
        },
        "required": [
          "g",
          "times",
          "per"
        ],
        "additionalProperties": false
      },
      "PlanInput": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlanItem"
            },
            "minItems": 1,
            "maxItems": 14,
            "uniqueItems": true
          },
          "until": {
            "type": "string",
            "format": "date"
          },
          "from": {
            "type": "string",
            "format": "date"
          },
          "message": {
            "type": "string",
            "maxLength": 240
          },
          "by": {
            "type": "string",
            "maxLength": 40
          },
          "assignmentId": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "expectedRevision": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000
          },
          "session": {
            "type": "integer",
            "minimum": 1,
            "maximum": 60
          },
          "goal": {
            "type": "object",
            "properties": {
              "days": {
                "type": "integer",
                "minimum": 1,
                "maximum": 7
              },
              "minutes": {
                "type": "integer",
                "minimum": 1,
                "maximum": 60
              }
            },
            "required": [
              "days",
              "minutes"
            ],
            "additionalProperties": false
          },
          "language": {
            "enum": [
              "en",
              "es"
            ]
          }
        },
        "required": [
          "items",
          "until"
        ],
        "additionalProperties": false
      },
      "PlayPlan": {
        "type": "object",
        "properties": {
          "t": {
            "const": "play-plan"
          },
          "v": {
            "const": 2
          },
          "id": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "revision": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000
          },
          "made": {
            "type": "string",
            "format": "date-time"
          },
          "clinic": {
            "type": "string",
            "maxLength": 60
          },
          "by": {
            "type": "string",
            "maxLength": 40
          },
          "child": {
            "type": "string",
            "maxLength": 40
          },
          "from": {
            "type": "string",
            "format": "date"
          },
          "until": {
            "type": "string",
            "format": "date"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlanItem"
            },
            "minItems": 1,
            "maxItems": 14,
            "uniqueItems": true
          },
          "msg": {
            "type": "string",
            "maxLength": 240
          },
          "session": {
            "type": "integer",
            "minimum": 1,
            "maximum": 60
          },
          "goal": {
            "type": "object",
            "properties": {
              "days": {
                "type": "integer",
                "minimum": 1,
                "maximum": 7
              },
              "minutes": {
                "type": "integer",
                "minimum": 1,
                "maximum": 60
              }
            },
            "required": [
              "days",
              "minutes"
            ],
            "additionalProperties": false
          },
          "language": {
            "enum": [
              "en",
              "es"
            ]
          }
        },
        "required": [
          "t",
          "v",
          "id",
          "revision",
          "made",
          "clinic",
          "by",
          "child",
          "from",
          "until",
          "items",
          "msg"
        ],
        "additionalProperties": false
      },
      "Assignment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "owner": {
            "type": "string"
          },
          "tenant": {
            "type": "string"
          },
          "parent": {
            "type": "string"
          },
          "childId": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "revision": {
            "type": "integer",
            "minimum": 1
          },
          "acceptedRevision": {
            "type": "integer",
            "minimum": 0
          },
          "payload": {
            "$ref": "#/components/schemas/PlayPlan"
          }
        },
        "required": [
          "id",
          "parent",
          "childId",
          "revision",
          "acceptedRevision",
          "payload"
        ],
        "additionalProperties": false
      },
      "Connection": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "owner": {
            "type": "string"
          },
          "tenant": {
            "type": "string"
          },
          "parent": {
            "type": "string"
          },
          "childId": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "nickname": {
            "type": "string",
            "maxLength": 40
          },
          "consentVersion": {
            "type": "string",
            "maxLength": 40
          },
          "consentAt": {
            "type": "string",
            "format": "date-time"
          },
          "revoked": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "childId",
          "nickname",
          "consentVersion",
          "consentAt",
          "revoked"
        ],
        "additionalProperties": false
      },
      "Review": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "owner": {
            "type": "string"
          },
          "tenant": {
            "type": "string"
          },
          "parent": {
            "type": "string"
          },
          "note": {
            "type": "string",
            "maxLength": 400
          }
        },
        "required": [
          "id",
          "parent",
          "note"
        ],
        "additionalProperties": false
      },
      "ReportMetadata": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "owner": {
            "type": "string"
          },
          "tenant": {
            "type": "string"
          },
          "parent": {
            "type": "string"
          },
          "childId": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "digest": {
            "type": "string",
            "maxLength": 64
          },
          "source": {
            "const": "family-upload"
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time"
          },
          "made": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "parent",
          "childId",
          "digest",
          "source",
          "receivedAt",
          "made"
        ],
        "additionalProperties": false
      },
      "CubReport": {
        "type": "object",
        "properties": {
          "t": {
            "const": "cub-report"
          },
          "v": {
            "enum": [
              1,
              2
            ]
          },
          "id": {
            "type": "string",
            "maxLength": 10,
            "minLength": 10
          },
          "made": {
            "type": "string",
            "format": "date-time"
          },
          "child": {
            "type": "object",
            "properties": {
              "nick": {
                "type": "string",
                "maxLength": 40
              },
              "ageMonths": {
                "type": "integer",
                "minimum": 0,
                "maximum": 240
              }
            },
            "required": [],
            "additionalProperties": false
          },
          "app": {
            "const": "cubbys-corner"
          },
          "appVersion": {
            "type": "string",
            "maxLength": 40
          },
          "band": {
            "type": "string",
            "maxLength": 40
          },
          "areas": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "d": {
                  "enum": [
                    "gross-motor",
                    "fine-motor",
                    "sensory",
                    "self-care",
                    "speech",
                    "language",
                    "social",
                    "feeding"
                  ]
                },
                "s": {
                  "enum": [
                    "strength",
                    "growing",
                    "talk"
                  ]
                }
              },
              "required": [
                "d",
                "s"
              ],
              "additionalProperties": false
            },
            "maxItems": 8
          },
          "flags": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 60
            },
            "maxItems": 80
          },
          "concerns": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 60
            },
            "maxItems": 24
          },
          "plays": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "g": {
                  "type": "string",
                  "maxLength": 60
                },
                "n": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 100000
                },
                "lvl": {
                  "type": "integer",
                  "minimum": 1
                },
                "best": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 3
                }
              },
              "required": [
                "g",
                "n",
                "lvl",
                "best"
              ],
              "additionalProperties": false
            },
            "maxItems": 400
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "g": {
                  "type": "string",
                  "maxLength": 60
                },
                "d": {
                  "enum": [
                    "gross-motor",
                    "fine-motor",
                    "sensory",
                    "self-care",
                    "speech",
                    "language",
                    "social",
                    "feeding"
                  ]
                },
                "saw": {
                  "type": "string",
                  "maxLength": 400
                },
                "tip": {
                  "type": "string",
                  "maxLength": 400
                }
              },
              "required": [
                "g",
                "d",
                "saw",
                "tip"
              ],
              "additionalProperties": false
            },
            "maxItems": 40
          },
          "plan": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "maxLength": 40
              },
              "revision": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10000
              },
              "done": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 10000
                }
              },
              "stats": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "g": {
                      "type": "string",
                      "maxLength": 60
                    },
                    "seconds": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 10000000
                    },
                    "n": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 100000
                    },
                    "stars": {
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 300000
                    },
                    "lvl": {
                      "type": "integer",
                      "minimum": 1
                    }
                  },
                  "required": [
                    "g",
                    "seconds",
                    "n",
                    "stars",
                    "lvl"
                  ],
                  "additionalProperties": false
                },
                "maxItems": 400
              },
              "days": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "d": {
                      "type": "string",
                      "format": "date"
                    },
                    "seconds": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 86400
                    }
                  },
                  "required": [
                    "d",
                    "seconds"
                  ],
                  "additionalProperties": false
                },
                "maxItems": 35
              },
              "daysTotal": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100000
              }
            },
            "required": [
              "id",
              "done"
            ],
            "additionalProperties": false
          },
          "grownup": {
            "type": "string",
            "maxLength": 240
          }
        },
        "required": [
          "t",
          "v",
          "id",
          "made",
          "child",
          "app",
          "appVersion",
          "band",
          "areas",
          "flags",
          "concerns",
          "plays",
          "notes"
        ],
        "additionalProperties": false
      },
      "Report": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "owner": {
            "type": "string"
          },
          "tenant": {
            "type": "string"
          },
          "parent": {
            "type": "string"
          },
          "childId": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "digest": {
            "type": "string",
            "maxLength": 64
          },
          "source": {
            "const": "family-upload"
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time"
          },
          "payload": {
            "$ref": "#/components/schemas/CubReport"
          },
          "reviews": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Review"
            }
          }
        },
        "required": [
          "id",
          "parent",
          "childId",
          "digest",
          "payload",
          "reviews"
        ],
        "additionalProperties": false
      },
      "Dashboard": {
        "type": "object",
        "properties": {
          "clinic": {
            "type": "object"
          },
          "connections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Connection"
            }
          },
          "assignments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Assignment"
            }
          },
          "reports": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReportMetadata"
            }
          },
          "clients": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "sponsors": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        },
        "required": [
          "clinic",
          "connections",
          "assignments",
          "reports",
          "clients",
          "sponsors"
        ],
        "additionalProperties": false
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "owner": {
            "type": "string"
          },
          "tenant": {
            "type": "string"
          },
          "parent": {
            "type": "string"
          },
          "type": {
            "enum": [
              "connection.approved",
              "connection.revoked",
              "plan.assigned",
              "plan.accepted",
              "report.shared"
            ]
          },
          "resource": {
            "type": "string",
            "maxLength": 32,
            "pattern": "^[a-f0-9]{32}$"
          }
        },
        "required": [
          "id",
          "tenant",
          "type",
          "resource",
          "created"
        ],
        "additionalProperties": false
      },
      "EventPage": {
        "type": "object",
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            }
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "hasMore": {
            "type": "boolean"
          }
        },
        "required": [
          "events",
          "nextCursor",
          "hasMore"
        ],
        "additionalProperties": false
      }
    }
  }
}
